diff options
188 files changed, 5720 insertions, 1894 deletions
diff --git a/api/current.xml b/api/current.xml index b38176d..68a1c76 100644 --- a/api/current.xml +++ b/api/current.xml @@ -2825,6 +2825,28 @@ visibility="public" > </field> +<field name="detailColumn" + type="int" + transient="false" + volatile="false" + value="16843427" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="detailSocialSummary" + type="int" + transient="false" + volatile="false" + value="16843428" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="dial" type="int" transient="false" @@ -7522,6 +7544,17 @@ visibility="public" > </field> +<field name="summaryColumn" + type="int" + transient="false" + volatile="false" + value="16843426" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="summaryOff" type="int" transient="false" @@ -7841,6 +7874,28 @@ visibility="public" > </field> +<field name="textAppearanceSearchResultSubtitle" + type="int" + transient="false" + volatile="false" + value="16843424" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="textAppearanceSearchResultTitle" + type="int" + transient="false" + volatile="false" + value="16843425" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="textAppearanceSmall" type="int" transient="false" @@ -31692,6 +31747,29 @@ <parameter name="intent" type="android.content.Intent"> </parameter> </method> +<method name="sendStickyOrderedBroadcast" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="resultReceiver" type="android.content.BroadcastReceiver"> +</parameter> +<parameter name="scheduler" type="android.os.Handler"> +</parameter> +<parameter name="initialCode" type="int"> +</parameter> +<parameter name="initialData" type="java.lang.String"> +</parameter> +<parameter name="initialExtras" type="android.os.Bundle"> +</parameter> +</method> <method name="setTheme" return="void" abstract="true" @@ -33028,6 +33106,29 @@ <parameter name="intent" type="android.content.Intent"> </parameter> </method> +<method name="sendStickyOrderedBroadcast" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="resultReceiver" type="android.content.BroadcastReceiver"> +</parameter> +<parameter name="scheduler" type="android.os.Handler"> +</parameter> +<parameter name="initialCode" type="int"> +</parameter> +<parameter name="initialData" type="java.lang.String"> +</parameter> +<parameter name="initialExtras" type="android.os.Bundle"> +</parameter> +</method> <method name="setTheme" return="void" abstract="false" @@ -35761,17 +35862,6 @@ visibility="public" > </field> -<field name="ACTION_REMOTE_INTENT" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.intent.action.REMOTE_INTENT"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="ACTION_RUN" type="java.lang.String" transient="false" @@ -36684,6 +36774,17 @@ visibility="public" > </field> +<field name="FLAG_ACTIVITY_NO_ANIMATION" + type="int" + transient="false" + volatile="false" + value="65536" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_ACTIVITY_NO_HISTORY" type="int" transient="false" @@ -36805,6 +36906,17 @@ visibility="public" > </field> +<field name="METADATA_DOCK_HOME" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.dock_home"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="URI_INTENT_SCHEME" type="int" transient="false" @@ -61078,6 +61190,17 @@ visibility="public" > </field> +<field name="YCbCr_422_I" + type="int" + transient="false" + volatile="false" + value="20" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="YCbCr_422_SP" type="int" transient="false" @@ -67260,6 +67383,17 @@ visibility="public" > </method> +<method name="lock" + return="void" + abstract="false" + native="true" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="open" return="android.hardware.Camera" abstract="false" @@ -67420,6 +67554,17 @@ <parameter name="jpeg" type="android.hardware.Camera.PictureCallback"> </parameter> </method> +<method name="unlock" + return="void" + abstract="false" + native="true" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <field name="CAMERA_ERROR_SERVER_DIED" type="int" transient="false" @@ -67554,6 +67699,17 @@ visibility="public" > </method> +<method name="getFocusMode" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getInt" return="int" abstract="false" @@ -67699,6 +67855,17 @@ visibility="public" > </method> +<method name="getSupportedFocusModes" + return="java.util.List<java.lang.String>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getSupportedPictureFormats" return="java.util.List<java.lang.Integer>" abstract="false" @@ -67880,6 +68047,19 @@ <parameter name="value" type="java.lang.String"> </parameter> </method> +<method name="setFocusMode" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="value" type="java.lang.String"> +</parameter> +</method> <method name="setGpsAltitude" return="void" abstract="false" @@ -68281,6 +68461,61 @@ visibility="public" > </field> +<field name="FLASH_MODE_VIDEO_LIGHT" + type="java.lang.String" + transient="false" + volatile="false" + value=""video-light"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FOCUS_MODE_AUTO" + type="java.lang.String" + transient="false" + volatile="false" + value=""auto"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FOCUS_MODE_FIXED" + type="java.lang.String" + transient="false" + volatile="false" + value=""fixed"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FOCUS_MODE_INFINITY" + type="java.lang.String" + transient="false" + volatile="false" + value=""infinity"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FOCUS_MODE_MACRO" + type="java.lang.String" + transient="false" + volatile="false" + value=""macro"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SCENE_MODE_ACTION" type="java.lang.String" transient="false" @@ -98695,6 +98930,116 @@ visibility="public" > </field> +<field name="EXTRA_HEALTH" + type="java.lang.String" + transient="false" + volatile="false" + value=""health"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_ICON_SMALL" + type="java.lang.String" + transient="false" + volatile="false" + value=""icon-small"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_LEVEL" + type="java.lang.String" + transient="false" + volatile="false" + value=""level"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_PLUGGED" + type="java.lang.String" + transient="false" + volatile="false" + value=""plugged"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_PRESENT" + type="java.lang.String" + transient="false" + volatile="false" + value=""present"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_SCALE" + type="java.lang.String" + transient="false" + volatile="false" + value=""scale"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_STATUS" + type="java.lang.String" + transient="false" + volatile="false" + value=""status"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_TECHNOLOGY" + type="java.lang.String" + transient="false" + volatile="false" + value=""technology"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_TEMPERATURE" + type="java.lang.String" + transient="false" + volatile="false" + value=""temperature"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_VOLTAGE" + type="java.lang.String" + transient="false" + volatile="false" + value=""voltage"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="Binder" extends="java.lang.Object" @@ -126684,6 +127029,29 @@ <parameter name="intent" type="android.content.Intent"> </parameter> </method> +<method name="sendStickyOrderedBroadcast" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="resultReceiver" type="android.content.BroadcastReceiver"> +</parameter> +<parameter name="scheduler" type="android.os.Handler"> +</parameter> +<parameter name="initialCode" type="int"> +</parameter> +<parameter name="initialData" type="java.lang.String"> +</parameter> +<parameter name="initialExtras" type="android.os.Bundle"> +</parameter> +</method> <method name="setTheme" return="void" abstract="false" @@ -134507,6 +134875,21 @@ <parameter name="addr" type="int"> </parameter> </method> +<method name="formatShortFileSize" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="number" type="long"> +</parameter> +</method> </class> <class name="Time" extends="java.lang.Object" @@ -160056,6 +160439,17 @@ visibility="public" > </field> +<field name="FLAG_DISMISS_KEYGUARD" + type="int" + transient="false" + volatile="false" + value="4194304" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_DITHER" type="int" transient="false" diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk index 8804636..15a199f 100644 --- a/cmds/keystore/Android.mk +++ b/cmds/keystore/Android.mk @@ -1,22 +1,36 @@ +# +# Copyright (C) 2009 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. +# + ifneq ($(TARGET_SIMULATOR),true) LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - netkeystore.c netkeystore_main.c keymgmt.c - -LOCAL_C_INCLUDES := \ - $(call include-path-for, system-core)/cutils \ - external/openssl/include - -LOCAL_SHARED_LIBRARIES := \ - libcutils libssl - -LOCAL_STATIC_LIBRARIES := +include $(CLEAR_VARS) +LOCAL_SRC_FILES := keystore.c +LOCAL_C_INCLUDES := external/openssl/include +LOCAL_SHARED_LIBRARIES := libcutils libcrypto LOCAL_MODULE:= keystore +include $(BUILD_EXECUTABLE) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := keystore_cli.c +LOCAL_C_INCLUDES := external/openssl/include +LOCAL_SHARED_LIBRARIES := libcutils libcrypto +LOCAL_MODULE:= keystore_cli +LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) -endif # !simulator)) +endif diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c index 2bcba97..ba74c78 100644 --- a/cmds/keystore/keystore.c +++ b/cmds/keystore/keystore.c @@ -125,6 +125,12 @@ static int recv_message(uint8_t *message, int length) return length; } +static int recv_end_of_file() +{ + uint8_t byte; + return recv(the_socket, &byte, 1, 0) == 0; +} + static void send_code(int8_t code) { send(the_socket, &code, 1, 0); @@ -217,8 +223,10 @@ static int8_t decrypt_blob(char *name, AES_KEY *aes_key) /* Here are the actions. Each of them is a function without arguments. All * information is defined in global variables, which are set properly before * performing an action. The number of parameters required by each action is - * fixed and defined in a table. Note that the lengths of parameters are checked - * when they are received, so boundary checks on parameters are omitted. */ + * fixed and defined in a table. If the return value of an action is positive, + * it will be treated as a response code and transmitted to the client. Note + * that the lengths of parameters are checked when they are received, so + * boundary checks on parameters are omitted. */ #define MAX_PARAM 2 #define MAX_RETRY 4 @@ -283,7 +291,7 @@ static int8_t exist() return NO_ERROR; } -static int8_t scan() +static int8_t saw() { DIR *dir = opendir("."); struct dirent *file; @@ -321,12 +329,10 @@ static int8_t reset() return SYSTEM_ERROR; } while ((file = readdir(dir)) != NULL) { - if (strcmp(".", file->d_name) || strcmp("..", file->d_name)) { - unlink(file->d_name); - } + unlink(file->d_name); } closedir(dir); - return UNINITIALIZED; + return NO_ERROR; } #define MASTER_KEY_FILE ".masterkey" @@ -358,7 +364,8 @@ static int8_t password() } if (n != NO_ERROR || blob.length != MASTER_KEY_SIZE) { if (retry <= 0) { - return reset(); + reset(); + return UNINITIALIZED; } return WRONG_PASSWORD + --retry; } @@ -387,7 +394,7 @@ static int8_t lock() memset(&encryption_key, 0, sizeof(encryption_key)); memset(&decryption_key, 0, sizeof(decryption_key)); state = LOCKED; - return LOCKED; + return NO_ERROR; } static int8_t unlock() @@ -404,7 +411,7 @@ enum perm { INSERT = 4, DELETE = 8, EXIST = 16, - SCAN = 32, + SAW = 32, RESET = 64, PASSWORD = 128, LOCK = 256, @@ -421,9 +428,9 @@ static struct action { {test, 't', 0, TEST, {0}}, {get, 'g', NO_ERROR, GET, {KEY_SIZE}}, {insert, 'i', NO_ERROR, INSERT, {KEY_SIZE, VALUE_SIZE}}, - {delete, 'd', NO_ERROR, DELETE, {KEY_SIZE}}, - {exist, 'e', NO_ERROR, EXIST, {KEY_SIZE}}, - {scan, 's', NO_ERROR, SCAN, {KEY_SIZE}}, + {delete, 'd', 0, DELETE, {KEY_SIZE}}, + {exist, 'e', 0, EXIST, {KEY_SIZE}}, + {saw, 's', 0, SAW, {KEY_SIZE}}, {reset, 'r', 0, RESET, {0}}, {password, 'p', 0, PASSWORD, {PASSWORD_SIZE, PASSWORD_SIZE}}, {lock, 'l', NO_ERROR, LOCK, {0}}, @@ -439,7 +446,7 @@ static struct user { {AID_SYSTEM, 0, ~GET}, {AID_VPN, AID_SYSTEM, GET}, {AID_WIFI, AID_SYSTEM, GET}, - {0, 0, TEST | GET | INSERT | DELETE | EXIST | SCAN}, + {0, 0, TEST | GET | INSERT | DELETE | EXIST | SAW}, }; static int8_t process(int8_t code) { @@ -471,6 +478,9 @@ static int8_t process(int8_t code) { return PROTOCOL_ERROR; } } + if (!recv_end_of_file()) { + return PROTOCOL_ERROR; + } return action->run(); } diff --git a/cmds/keystore/keystore_cli.c b/cmds/keystore/keystore_cli.c index b0b76ff..e8afb5a 100644 --- a/cmds/keystore/keystore_cli.c +++ b/cmds/keystore/keystore_cli.c @@ -53,8 +53,8 @@ int main(int argc, char **argv) return 0; } - sock = socket_local_client("keystore", - ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); + sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); if (sock == -1) { puts("Failed to connect"); return 1; diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h index 7665e81..0e7e1ae 100644 --- a/cmds/keystore/keystore_get.h +++ b/cmds/keystore/keystore_get.h @@ -1,53 +1,69 @@ /* -** -** Copyright 2009, 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. -*/ + * Copyright (C) 2009 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 __KEYSTORE_GET_H__ #define __KEYSTORE_GET_H__ #include <stdio.h> -#include <stdlib.h> +#include <stdint.h> #include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> -#include "certtool.h" +#include <cutils/sockets.h> -/* This function is provided to native components to get values from keystore. - * Users are required to link against libcutils. If something goes wrong, NULL - * is returned. Otherwise it returns the value in dynamically allocated memory - * and sets the size if the pointer is not NULL. One can release the memory by - * calling free(). */ -static char *keystore_get(const char *key, int *size) +#define KEYSTORE_MESSAGE_SIZE 65535 + +/* This function is provided for native components to get values from keystore. + * Users are required to link against libcutils. The lengths of keys and values + * are limited to KEYSTORE_MESSAGE_SIZE. This function returns the length of + * the requested value or -1 if something goes wrong. */ +static int keystore_get(const char *key, char *value) { - char buffer[MAX_KEY_VALUE_LENGTH]; - char *value; - int length; + int length = strlen(key); + uint8_t bytes[2] = {length >> 8, length}; + uint8_t code = 'g'; + int sock; - if (get_cert(key, (unsigned char *)buffer, &length) != 0) { - return NULL; + if (length > KEYSTORE_MESSAGE_SIZE) { + return -1; } - value = malloc(length + 1); - if (!value) { - return NULL; + sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (sock == -1) { + return -1; } - memcpy(value, buffer, length); - value[length] = 0; - if (size) { - *size = length; + if (send(sock, &code, 1, 0) == 1 && send(sock, bytes, 2, 0) == 2 && + send(sock, key, length, 0) == length && shutdown(sock, SHUT_WR) == 0 && + recv(sock, &code, 1, 0) == 1 && code == /* NO_ERROR */ 1 && + recv(sock, &bytes[0], 1, 0) == 1 && recv(sock, &bytes[1], 1, 0) == 1) { + int offset = 0; + length = bytes[0] << 8 | bytes[1]; + while (offset < length) { + int n = recv(sock, &value[offset], length - offset, 0); + if (n <= 0) { + length = -1; + break; + } + offset += n; + } } - return value; + close(sock); + return length; } #endif diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index a86fe90..4561899 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -790,8 +790,8 @@ public class Activity extends ContextThemeWrapper * @see #onPostCreate */ protected void onCreate(Bundle savedInstanceState) { - mVisibleFromClient = mWindow.getWindowStyle().getBoolean( - com.android.internal.R.styleable.Window_windowNoDisplay, true); + mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( + com.android.internal.R.styleable.Window_windowNoDisplay, false); mCalled = true; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8142d1a..6acd665 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1765,6 +1765,7 @@ public final class ActivityThread { public static final int CREATE_BACKUP_AGENT = 128; public static final int DESTROY_BACKUP_AGENT = 129; public static final int SUICIDE = 130; + public static final int REMOVE_PROVIDER = 131; String codeToString(int code) { if (localLOGV) { switch (code) { @@ -1799,6 +1800,7 @@ public final class ActivityThread { case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT"; case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT"; case SUICIDE: return "SUICIDE"; + case REMOVE_PROVIDER: return "REMOVE_PROVIDER"; } } return "(unknown)"; @@ -1911,9 +1913,10 @@ public final class ActivityThread { handleDestroyBackupAgent((CreateBackupAgentData)msg.obj); break; case SUICIDE: - { - Process.killProcess(Process.myPid()); - } + Process.killProcess(Process.myPid()); + break; + case REMOVE_PROVIDER: + completeRemoveProvider((IContentProvider)msg.obj); break; } } @@ -4029,15 +4032,28 @@ public final class ActivityThread { } else { prc.count--; if(prc.count == 0) { - mProviderRefCountMap.remove(jBinder); - //invoke removeProvider to dereference provider - removeProviderLocked(provider); + // Schedule the actual remove asynchronously, since we + // don't know the context this will be called in. + Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, provider); + mH.sendMessage(msg); } //end if } //end else } //end synchronized return true; } + final void completeRemoveProvider(IContentProvider provider) { + IBinder jBinder = provider.asBinder(); + synchronized(mProviderMap) { + ProviderRefCount prc = mProviderRefCountMap.get(jBinder); + if(prc != null && prc.count == 0) { + mProviderRefCountMap.remove(jBinder); + //invoke removeProvider to dereference provider + removeProviderLocked(provider); + } + } + } + public final void removeProviderLocked(IContentProvider provider) { if (provider == null) { return; diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index dc8d873..afafe64 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -659,6 +659,38 @@ class ApplicationContext extends Context { } @Override + public void sendStickyOrderedBroadcast(Intent intent, + BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { + IIntentReceiver rd = null; + if (resultReceiver != null) { + if (mPackageInfo != null) { + if (scheduler == null) { + scheduler = mMainThread.getHandler(); + } + rd = mPackageInfo.getReceiverDispatcher( + resultReceiver, getOuterContext(), scheduler, + mMainThread.getInstrumentation(), false); + } else { + if (scheduler == null) { + scheduler = mMainThread.getHandler(); + } + rd = new ActivityThread.PackageInfo.ReceiverDispatcher( + resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver(); + } + } + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + try { + ActivityManagerNative.getDefault().broadcastIntent( + mMainThread.getApplicationThread(), intent, resolvedType, rd, + initialCode, initialData, initialExtras, null, + true, true); + } catch (RemoteException e) { + } + } + + @Override public void removeStickyBroadcast(Intent intent) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); if (resolvedType != null) { diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index d05c9ab..a2c95f4 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -736,7 +736,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return false; } - if (keyCode == KeyEvent.KEYCODE_SEARCH) { + if (keyCode == KeyEvent.KEYCODE_SEARCH && event.getRepeatCount() == 0) { + event.startTracking(); // Consume search key for later use. return true; } @@ -1143,7 +1144,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS String query = mSearchAutoComplete.getText().toString(); String action = mGlobalSearchMode ? Intent.ACTION_WEB_SEARCH : Intent.ACTION_SEARCH; Intent intent = createIntent(action, null, null, query, null, - actionKey, actionMsg); + actionKey, actionMsg, null); // Allow GlobalSearch to log and create shortcut for searches launched by // the search button, enter key or an action key. if (mGlobalSearchMode) { @@ -1181,7 +1182,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // report back about the click if (mGlobalSearchMode) { // in global search mode, do it via cursor - mSuggestionsAdapter.callCursorOnClick(c, position); + mSuggestionsAdapter.callCursorOnClick(c, position, actionKey, actionMsg); } else if (intent != null && mPreviousComponents != null && !mPreviousComponents.isEmpty()) { @@ -1218,7 +1219,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction()); cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString()); cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME, - intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY)); + intent.getComponent().flattenToShortString()); // ensure the icons will work for global search cv.put(SearchManager.SUGGEST_COLUMN_ICON_1, @@ -1578,9 +1579,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); + String mode = mGlobalSearchMode ? SearchManager.MODE_GLOBAL_SEARCH_SUGGESTION : null; return createIntent(action, dataUri, extraData, query, componentName, actionKey, - actionMsg); + actionMsg, mode); } catch (RuntimeException e ) { int rowNum; try { // be really paranoid now @@ -1606,13 +1608,18 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. * @param actionMsg The message for the action key that was pressed, * or <code>null</code> if none. + * @param mode The search mode, one of the acceptable values for + * {@link SearchManager#SEARCH_MODE}, or {@code null}. * @return The intent. */ private Intent createIntent(String action, Uri data, String extraData, String query, - String componentName, int actionKey, String actionMsg) { + String componentName, int actionKey, String actionMsg, String mode) { // Now build the Intent Intent intent = new Intent(action); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // We need CLEAR_TOP to avoid reusing an old task that has other activities + // on top of the one we want. + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); if (data != null) { intent.setData(data); } @@ -1633,6 +1640,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS intent.putExtra(SearchManager.ACTION_KEY, actionKey); intent.putExtra(SearchManager.ACTION_MSG, actionMsg); } + if (mode != null) { + intent.putExtra(SearchManager.SEARCH_MODE, mode); + } // Only allow 3rd-party intents from GlobalSearch if (!mGlobalSearchMode) { intent.setComponent(mSearchable.getSearchActivity()); diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 3a14f6f..f0876f4 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1289,6 +1289,25 @@ public class SearchManager public final static String SOURCE = "source"; /** + * Intent extra data key: Use {@link android.content.Intent#getBundleExtra + * content.Intent.getBundleExtra(SEARCH_MODE)} to get the search mode used + * to launch the intent. + * The only current value for this is {@link #MODE_GLOBAL_SEARCH_SUGGESTION}. + * + * @hide + */ + public final static String SEARCH_MODE = "search_mode"; + + /** + * Value for the {@link #SEARCH_MODE} key. + * This is used if the intent was launched by clicking a suggestion in global search + * mode (Quick Search Box). + * + * @hide + */ + public static final String MODE_GLOBAL_SEARCH_SUGGESTION = "global_search_suggestion"; + + /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()} * to obtain the keycode that the user used to trigger this query. It will be zero if the @@ -1343,6 +1362,10 @@ public class SearchManager = "DialogCursorProtocol.CLICK.sendPosition"; public final static String CLICK_SEND_MAX_DISPLAY_POS = "DialogCursorProtocol.CLICK.sendDisplayPosition"; + public final static String CLICK_SEND_ACTION_KEY + = "DialogCursorProtocol.CLICK.sendActionKey"; + public final static String CLICK_SEND_ACTION_MSG + = "DialogCursorProtocol.CLICK.sendActionMsg"; public final static String CLICK_RECEIVE_SELECTED_POS = "DialogCursorProtocol.CLICK.receiveSelectedPosition"; diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java index 4f9531e..9234a9c 100644 --- a/core/java/android/app/SuggestionsAdapter.java +++ b/core/java/android/app/SuggestionsAdapter.java @@ -26,7 +26,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.Cursor; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; @@ -37,6 +36,7 @@ import android.text.Html; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; +import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.widget.Filter; @@ -47,7 +47,6 @@ import android.widget.TextView; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.util.List; import java.util.WeakHashMap; /** @@ -290,12 +289,16 @@ class SuggestionsAdapter extends ResourceCursorAdapter { * @param cursor The cursor * @param position The position that was clicked. */ - void callCursorOnClick(Cursor cursor, int position) { + void callCursorOnClick(Cursor cursor, int position, int actionKey, String actionMsg) { if (!mGlobalSearchMode) return; - final Bundle request = new Bundle(3); + final Bundle request = new Bundle(5); request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.CLICK); request.putInt(DialogCursorProtocol.CLICK_SEND_POSITION, position); request.putInt(DialogCursorProtocol.CLICK_SEND_MAX_DISPLAY_POS, mMaxDisplayed); + if (actionKey != KeyEvent.KEYCODE_UNKNOWN) { + request.putInt(DialogCursorProtocol.CLICK_SEND_ACTION_KEY, actionKey); + request.putString(DialogCursorProtocol.CLICK_SEND_ACTION_MSG, actionMsg); + } final Bundle response = cursor.respond(request); mMaxDisplayed = -1; mListItemToSelect = response.getInt( @@ -569,69 +572,91 @@ class SuggestionsAdapter extends ResourceCursorAdapter { if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) { return null; } - - // First, check the cache. - Drawable.ConstantState cached = mOutsideDrawablesCache.get(drawableId); - if (cached != null) { - if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + drawableId); - return cached.newDrawable(mProviderContext.getResources()); - } - - Drawable drawable = null; try { - // Not cached, try using it as a plain resource ID in the provider's context. + // First, see if it's just an integer int resourceId = Integer.parseInt(drawableId); + // It's an int, look for it in the cache + String drawableUri = ContentResolver.SCHEME_ANDROID_RESOURCE + + "://" + mProviderContext.getPackageName() + "/" + resourceId; + // Must use URI as cache key, since ints are app-specific + Drawable drawable = checkIconCache(drawableUri); + if (drawable != null) { + return drawable; + } + // Not cached, find it by resource ID drawable = mProviderContext.getResources().getDrawable(resourceId); + // Stick it in the cache, using the URI as key + storeInIconCache(drawableUri, drawable); + return drawable; } catch (NumberFormatException nfe) { - // The id was not an integer resource id, use it as a URI - try { - Uri uri = Uri.parse(drawableId); - String scheme = uri.getScheme(); - if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { - // Load drawables through Resources, to get the source density information - OpenResourceIdResult r = - mProviderContext.getContentResolver().getResourceId(uri); - try { - drawable = r.r.getDrawable(r.id); - } catch (Resources.NotFoundException ex) { - throw new FileNotFoundException("Resource does not exist: " + uri); - } - } else { - // Let the ContentResolver handle content and file URIs. - InputStream stream = mProviderContext.getContentResolver().openInputStream(uri); - if (stream == null) { - throw new FileNotFoundException("Failed to open " + uri); - } - try { - drawable = Drawable.createFromStream(stream, null); - } finally { - try { - stream.close(); - } catch (IOException ex) { - Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex); - } - } - } - } catch (FileNotFoundException fnfe) { - Log.w(LOG_TAG, "Icon not found: " + drawableId + ", " + fnfe.getMessage()); - // drawable = null; + // It's not an integer, use it as a URI + Drawable drawable = checkIconCache(drawableId); + if (drawable != null) { + return drawable; } + Uri uri = Uri.parse(drawableId); + drawable = getDrawable(uri); + storeInIconCache(drawableId, drawable); + return drawable; } catch (Resources.NotFoundException nfe) { + // It was an integer, but it couldn't be found, bail out Log.w(LOG_TAG, "Icon resource not found: " + drawableId); - // drawable = null; + return null; } + } - if (drawable == null) { - if (DBG) Log.d(LOG_TAG, "Didn't find icon: " + drawableId); - } else { - if (DBG) { - Log.d(LOG_TAG, "Found icon: " + drawableId); + /** + * Gets a drawable by URI, without using the cache. + * + * @return A drawable, or {@code null} if the drawable could not be loaded. + */ + private Drawable getDrawable(Uri uri) { + try { + String scheme = uri.getScheme(); + if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { + // Load drawables through Resources, to get the source density information + OpenResourceIdResult r = + mProviderContext.getContentResolver().getResourceId(uri); + try { + return r.r.getDrawable(r.id); + } catch (Resources.NotFoundException ex) { + throw new FileNotFoundException("Resource does not exist: " + uri); + } + } else { + // Let the ContentResolver handle content and file URIs. + InputStream stream = mProviderContext.getContentResolver().openInputStream(uri); + if (stream == null) { + throw new FileNotFoundException("Failed to open " + uri); + } + try { + return Drawable.createFromStream(stream, null); + } finally { + try { + stream.close(); + } catch (IOException ex) { + Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex); + } + } } - // Cache it so we don't do this lookup again - mOutsideDrawablesCache.put(drawableId, drawable.getConstantState()); + } catch (FileNotFoundException fnfe) { + Log.w(LOG_TAG, "Icon not found: " + uri + ", " + fnfe.getMessage()); + return null; } + } - return drawable; + private Drawable checkIconCache(String resourceUri) { + Drawable.ConstantState cached = mOutsideDrawablesCache.get(resourceUri); + if (cached == null) { + return null; + } + if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + resourceUri); + return cached.newDrawable(); + } + + private void storeInIconCache(String resourceUri, Drawable drawable) { + if (drawable != null) { + mOutsideDrawablesCache.put(resourceUri, drawable.getConstantState()); + } } /** diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 96a927b..1fc22fe 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -573,6 +573,7 @@ public final class BluetoothAdapter { /** * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" + * <p>Alphabetic characters must be uppercase to be valid. * * @param address Bluetooth address as string * @return true if the address is valid, false otherwise @@ -586,8 +587,9 @@ public final class BluetoothAdapter { switch (i % 3) { case 0: case 1: - if (Character.digit(c, 16) != -1) { - break; // hex character, OK + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { + // hex character, OK + break; } return false; case 2: diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index f81ba73..b52a822 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -28,6 +28,7 @@ import android.util.Log; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.util.UUID; /** * Represents a remote Bluetooth device. @@ -226,6 +227,20 @@ public final class BluetoothDevice implements Parcelable { public static final String EXTRA_PASSKEY = "android.bluetooth.device.extra.PASSKEY"; /** + * Broadcast Action: This intent is used to broadcast the {@link UUID} + * wrapped as a {@link ParcelUuid} of the remote device after it has been + * fetched. This intent is sent only when the UUIDs of the remote device + * are requested to be fetched using Service Discovery Protocol + * <p> Always contains the extra field {@link #EXTRA_DEVICE} + * <p> Always contains the extra filed {@link #EXTRA_UUID} + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_UUID = + "android.bleutooth.device.action.UUID"; + + /** * Broadcast Action: Indicates a failure to retrieve the name of a remote * device. * <p>Always contains the extra field {@link #EXTRA_DEVICE}. @@ -292,6 +307,15 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; + /** + * Used as an extra field in {@link #ACTION_UUID} intents, + * Contains the {@link ParcelUuid}s of the remote device which is a parcelable + * version of {@link UUID}. + * @hide + */ + public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; + + private static IBluetooth sService; /* Guarenteed constant after first object constructed */ private final String mAddress; @@ -507,6 +531,27 @@ public final class BluetoothDevice implements Parcelable { return null; } + /** + * Perform a SDP query on the remote device to get the UUIDs + * supported. This API is asynchronous and an Intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * an Intent is sent with the UUIDs that is currently present in the + * cache. Clients should use the {@link getUuids} to get UUIDs + * is SDP is not to be performed. + * + * @return False if the sanity check fails, True if the process + * of initiating an ACL connection to the remote device + * was started. + * @hide + */ + public boolean fetchUuidsWithSdp() { + try { + return sService.fetchRemoteUuidsWithSdp(mAddress); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** @hide */ public int getServiceChannel(ParcelUuid uuid) { try { diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index 409c744..24ad06a 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -83,6 +83,12 @@ public final class BluetoothUuid { * @param uuid */ public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { + if ((uuidArray == null || uuidArray.length == 0) && uuid == null) + return true; + + if (uuidArray == null) + return false; + for (ParcelUuid element: uuidArray) { if (element.equals(uuid)) return true; } @@ -98,7 +104,14 @@ public final class BluetoothUuid { */ public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; - if (uuidA == null || uuidB == null) return false; + + if (uuidA == null) { + return uuidB.length == 0 ? true : false; + } + + if (uuidB == null) { + return uuidA.length == 0 ? true : false; + } HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid> (Arrays.asList(uuidA)); for (ParcelUuid uuid: uuidB) { @@ -117,7 +130,12 @@ public final class BluetoothUuid { */ public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; - if (uuidA == null || uuidB == null) return false; + + if (uuidA == null) { + return uuidB.length == 0 ? true : false; + } + + if (uuidB == null) return true; HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid> (Arrays.asList(uuidA)); for (ParcelUuid uuid: uuidB) { diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 04c8ec9..203a61d 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -53,6 +53,7 @@ interface IBluetooth String getRemoteName(in String address); int getRemoteClass(in String address); ParcelUuid[] getRemoteUuids(in String address); + boolean fetchRemoteUuidsWithSdp(in String address); int getRemoteServiceChannel(in String address,in ParcelUuid uuid); boolean setPin(in String address, in byte[] pin); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index a3c4f9a..fe4665e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -657,8 +657,7 @@ public abstract class Context { * supplying your own BroadcastReceiver when calling, which will be * treated as a final receiver at the end of the broadcast -- its * {@link BroadcastReceiver#onReceive} method will be called with - * the result values collected from the other receivers. If you use - * an <var>resultReceiver</var> with this method, then the broadcast will + * the result values collected from the other receivers. The broadcast will * be serialized in the same way as calling * {@link #sendOrderedBroadcast(Intent, String)}. * @@ -689,6 +688,7 @@ public abstract class Context { * @see #sendBroadcast(Intent, String) * @see #sendOrderedBroadcast(Intent, String) * @see #sendStickyBroadcast(Intent) + * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) * @see android.content.BroadcastReceiver * @see #registerReceiver * @see android.app.Activity#RESULT_OK @@ -715,8 +715,55 @@ public abstract class Context { * be re-broadcast to future receivers. * * @see #sendBroadcast(Intent) + * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) */ public abstract void sendStickyBroadcast(Intent intent); + + /** + * Version of {@link #sendStickyBroadcast} that allows you to + * receive data back from the broadcast. This is accomplished by + * supplying your own BroadcastReceiver when calling, which will be + * treated as a final receiver at the end of the broadcast -- its + * {@link BroadcastReceiver#onReceive} method will be called with + * the result values collected from the other receivers. The broadcast will + * be serialized in the same way as calling + * {@link #sendOrderedBroadcast(Intent, String)}. + * + * <p>Like {@link #sendBroadcast(Intent)}, this method is + * asynchronous; it will return before + * resultReceiver.onReceive() is called. Note that the sticky data + * stored is only the data you initially supply to the broadcast, not + * the result of any changes made by the receivers. + * + * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param resultReceiver Your own BroadcastReceiver to treat as the final + * receiver of the broadcast. + * @param scheduler A custom Handler with which to schedule the + * resultReceiver callback; if null it will be + * scheduled in the Context's main thread. + * @param initialCode An initial value for the result code. Often + * Activity.RESULT_OK. + * @param initialData An initial value for the result data. Often + * null. + * @param initialExtras An initial value for the result extras. Often + * null. + * + * @see #sendBroadcast(Intent) + * @see #sendBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendStickyBroadcast(Intent) + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see android.app.Activity#RESULT_OK + */ + public abstract void sendStickyOrderedBroadcast(Intent intent, + BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras); + /** * Remove the data previously sent with {@link #sendStickyBroadcast}, diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index d580c47..1b34320 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -288,6 +288,16 @@ public class ContextWrapper extends Context { } @Override + public void sendStickyOrderedBroadcast( + Intent intent, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { + mBase.sendStickyOrderedBroadcast(intent, + resultReceiver, scheduler, initialCode, + initialData, initialExtras); + } + + @Override public void removeStickyBroadcast(Intent intent) { mBase.removeStickyBroadcast(intent); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 1359761..5fb5768 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1352,14 +1352,20 @@ public class Intent implements Parcelable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED"; /** - * Broadcast Action: The charging state, or charge level of the battery has - * changed. + * Broadcast Action: This is a <em>sticky broadcast</em> containing the + * charging state, level, and other information about the battery. + * See {@link android.os.BatteryManager} for documentation on the + * contents of the Intent. * * <p class="note"> * You can <em>not</em> receive this through components declared * in manifests, only by explicitly registering for it with * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) - * Context.registerReceiver()}. + * Context.registerReceiver()}. See {@link #ACTION_BATTERY_LOW}, + * {@link #ACTION_BATTERY_OKAY}, {@link #ACTION_POWER_CONNECTED}, + * and {@link #ACTION_POWER_DISCONNECTED} for distinct battery-related + * broadcasts that are sent and can be received through manifest + * receivers. * * <p class="note">This is a protected intent that can only be sent * by the system. @@ -1434,7 +1440,8 @@ public class Intent implements Parcelable { */ public static final String ACTION_REQUEST_SHUTDOWN = "android.intent.action.ACTION_REQUEST_SHUTDOWN"; /** - * Broadcast Action: Indicates low memory condition on the device + * Broadcast Action: A sticky broadcast that indicates low memory + * condition on the device * * <p class="note">This is a protected intent that can only be sent * by the system. @@ -1711,6 +1718,18 @@ public class Intent implements Parcelable { "android.intent.action.REBOOT"; /** + * Broadcast Action: A sticky broadcast indicating the phone was docked + * or undocked. Includes the extra + * field {@link #EXTRA_DOCK_STATE}, containing the current dock state. + * This is intended for monitoring the current dock state. + * To launch an activity from a dock state change, use {@link #CATEGORY_CAR_DOCK} + * or {@link #CATEGORY_DESK_DOCK} instead. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DOCK_EVENT = + "android.intent.action.DOCK_EVENT"; + + /** * Broadcast Action: a remote intent is to be broadcasted. * * A remote intent is used for remote RPC between devices. The remote intent @@ -1720,6 +1739,7 @@ public class Intent implements Parcelable { * does not trust intent broadcasts from arbitrary intent senders, it should require * the sender to hold certain permissions so only trusted sender's broadcast will be * let through. + * @hide */ public static final String ACTION_REMOTE_INTENT = "android.intent.action.REMOTE_INTENT"; @@ -1865,7 +1885,7 @@ public class Intent implements Parcelable { "android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"; /** * An activity to run when device is inserted into a car dock. - * Used with {@link #ACTION_MAIN} to launch an activity. + * Used with {@link #ACTION_MAIN} to launch an activity. * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead. */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) @@ -1878,16 +1898,6 @@ public class Intent implements Parcelable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK"; - /** - * Broadcast Action: The phone was docked or undocked. Includes the extra - * field {@link #EXTRA_DOCK_STATE}, containing the current dock state. - * This is intended for monitoring the current dock state. - * To launch an activity from a dock state change, use {@link #CATEGORY_CAR_DOCK} - * or {@link #CATEGORY_DESK_DOCK} instead. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_DOCK_EVENT = "android.intent.action.DOCK_EVENT"; - // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard extra data keys. @@ -2046,6 +2056,12 @@ public class Intent implements Parcelable { public static final int EXTRA_DOCK_STATE_CAR = 2; /** + * Boolean that can be supplied as meta-data with a dock activity, to + * indicate that the dock should take over the home key when it is active. + */ + public static final String METADATA_DOCK_HOME = "android.dock_home"; + + /** * Used as a parcelable extra field in {@link #ACTION_APP_ERROR}, containing * the bug report. * @@ -2308,6 +2324,18 @@ public class Intent implements Parcelable { */ public static final int FLAG_ACTIVITY_REORDER_TO_FRONT = 0X00020000; /** + * If set in an Intent passed to {@link Context#startActivity Context.startActivity()}, + * this flag will prevent the system from applying an activity transition + * animation to go to the next activity state. This doesn't mean an + * animation will never run -- if another activity change happens that doesn't + * specify this flag before the activity started here is displayed, then + * that transition will be used. This this flag can be put to good use + * when you are going to do a series of activity operations but the + * animation seen by the user shouldn't be driven by the first activity + * change but rather a later one. + */ + public static final int FLAG_ACTIVITY_NO_ANIMATION = 0X00010000; + /** * If set, when sending a broadcast only registered receivers will be * called -- no BroadcastReceiver components will be launched. */ @@ -2521,6 +2549,7 @@ public class Intent implements Parcelable { * * @param uri The URI to turn into an Intent. * @param flags Additional processing flags. Either 0 or + * {@link #URI_INTENT_SCHEME}. * * @return Intent The newly created Intent object. * @@ -2654,24 +2683,24 @@ public class Intent implements Parcelable { int i = uri.lastIndexOf('#'); if (i >= 0) { - Uri data = null; String action = null; - if (i > 0) { - data = Uri.parse(uri.substring(0, i)); - } + final int intentFragmentStart = i; + boolean isIntentFragment = false; i++; if (uri.regionMatches(i, "action(", 0, 7)) { + isIntentFragment = true; i += 7; int j = uri.indexOf(')', i); action = uri.substring(i, j); i = j + 1; } - intent = new Intent(action, data); + intent = new Intent(action); if (uri.regionMatches(i, "categories(", 0, 11)) { + isIntentFragment = true; i += 11; int j = uri.indexOf(')', i); while (i < j) { @@ -2686,6 +2715,7 @@ public class Intent implements Parcelable { } if (uri.regionMatches(i, "type(", 0, 5)) { + isIntentFragment = true; i += 5; int j = uri.indexOf(')', i); intent.mType = uri.substring(i, j); @@ -2693,6 +2723,7 @@ public class Intent implements Parcelable { } if (uri.regionMatches(i, "launchFlags(", 0, 12)) { + isIntentFragment = true; i += 12; int j = uri.indexOf(')', i); intent.mFlags = Integer.decode(uri.substring(i, j)).intValue(); @@ -2700,6 +2731,7 @@ public class Intent implements Parcelable { } if (uri.regionMatches(i, "component(", 0, 10)) { + isIntentFragment = true; i += 10; int j = uri.indexOf(')', i); int sep = uri.indexOf('!', i); @@ -2712,6 +2744,7 @@ public class Intent implements Parcelable { } if (uri.regionMatches(i, "extras(", 0, 7)) { + isIntentFragment = true; i += 7; final int closeParen = uri.indexOf(')', i); @@ -2783,6 +2816,12 @@ public class Intent implements Parcelable { } } + if (isIntentFragment) { + intent.mData = Uri.parse(uri.substring(0, intentFragmentStart)); + } else { + intent.mData = Uri.parse(uri); + } + if (intent.mAction == null) { // By default, if no action is specified, then use VIEW. intent.mAction = ACTION_VIEW; @@ -3572,7 +3611,7 @@ public class Intent implements Parcelable { } } else { ResolveInfo info = pm.resolveActivity( - this, PackageManager.MATCH_DEFAULT_ONLY); + this, PackageManager.MATCH_DEFAULT_ONLY | flags); if (info != null) { ai = info.activityInfo; } diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 7859d5a..80613d8 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -514,7 +514,7 @@ class SyncManager implements OnAccountsUpdatedListener { public SyncStorageEngine getSyncStorageEngine() { return mSyncStorageEngine; } - + private void ensureAlarmService() { if (mAlarmService == null) { mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); @@ -1125,7 +1125,7 @@ class SyncManager implements OnAccountsUpdatedListener { tobj.set(time); return tobj.format("%Y-%m-%d %H:%M:%S"); } - + protected void dumpSyncState(PrintWriter pw, StringBuilder sb) { pw.print("sync enabled: "); pw.println(isSyncEnabled()); pw.print("data connected: "); pw.println(mDataConnectionIsConnected); @@ -1219,13 +1219,13 @@ class SyncManager implements OnAccountsUpdatedListener { = mSyncStorageEngine.getAuthority(status.authorityId); if (authority != null) { Account curAccount = authority.account; - + if (processedAccounts.contains(curAccount)) { continue; } - + processedAccounts.add(curAccount); - + pw.print(" Account "); pw.print(authority.account.name); pw.print(" "); pw.print(authority.account.type); pw.println(":"); @@ -1271,7 +1271,7 @@ class SyncManager implements OnAccountsUpdatedListener { pw.print(time/1000); pw.print('.'); pw.print((time/100)%10); pw.print('s'); } - + private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) { pw.print("Success ("); pw.print(ds.successCount); if (ds.successCount > 0) { @@ -1285,7 +1285,7 @@ class SyncManager implements OnAccountsUpdatedListener { } pw.println(")"); } - + protected void dumpSyncHistory(PrintWriter pw, StringBuilder sb) { SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics(); if (dses != null && dses[0] != null) { @@ -1295,18 +1295,18 @@ class SyncManager implements OnAccountsUpdatedListener { int today = dses[0].day; int i; SyncStorageEngine.DayStats ds; - + // Print each day in the current week. for (i=1; i<=6 && i < dses.length; i++) { ds = dses[i]; if (ds == null) break; int delta = today-ds.day; if (delta > 6) break; - + pw.print(" Day-"); pw.print(delta); pw.print(": "); dumpDayStatistic(pw, ds); } - + // Aggregate all following days into weeks and print totals. int weekDay = today; while (i < dses.length) { @@ -1321,7 +1321,7 @@ class SyncManager implements OnAccountsUpdatedListener { int delta = weekDay-ds.day; if (delta > 6) break; i++; - + if (aggr == null) { aggr = new SyncStorageEngine.DayStats(weekDay); } @@ -1336,7 +1336,7 @@ class SyncManager implements OnAccountsUpdatedListener { } } } - + ArrayList<SyncStorageEngine.SyncHistoryItem> items = mSyncStorageEngine.getSyncHistory(); if (items != null && items.size() > 0) { @@ -2132,7 +2132,8 @@ class SyncManager implements OnAccountsUpdatedListener { final long now = System.currentTimeMillis(); EventLog.writeEvent(2720, syncOperation.authority, - SyncStorageEngine.EVENT_START, source); + SyncStorageEngine.EVENT_START, source, + syncOperation.account.name.hashCode()); return mSyncStorageEngine.insertStartSyncEvent( syncOperation.account, syncOperation.authority, now, source); @@ -2141,7 +2142,8 @@ class SyncManager implements OnAccountsUpdatedListener { public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, int upstreamActivity, int downstreamActivity, long elapsedTime) { EventLog.writeEvent(2720, syncOperation.authority, - SyncStorageEngine.EVENT_STOP, syncOperation.syncSource); + SyncStorageEngine.EVENT_STOP, syncOperation.syncSource, + syncOperation.account.name.hashCode()); mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, resultMessage, downstreamActivity, upstreamActivity); @@ -2173,7 +2175,7 @@ class SyncManager implements OnAccountsUpdatedListener { syncOperation.pendingOperation = op; add(syncOperation, op); } - + if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */); } diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java index 6687fcb..b8fda03 100644 --- a/core/java/android/content/SyncStatusInfo.java +++ b/core/java/android/content/SyncStatusInfo.java @@ -38,6 +38,7 @@ public class SyncStatusInfo implements Parcelable { public String lastFailureMesg; public long initialFailureTime; public boolean pending; + public boolean initialize; SyncStatusInfo(int authorityId) { this.authorityId = authorityId; @@ -73,6 +74,7 @@ public class SyncStatusInfo implements Parcelable { parcel.writeString(lastFailureMesg); parcel.writeLong(initialFailureTime); parcel.writeInt(pending ? 1 : 0); + parcel.writeInt(initialize ? 1 : 0); } SyncStatusInfo(Parcel parcel) { @@ -94,6 +96,7 @@ public class SyncStatusInfo implements Parcelable { lastFailureMesg = parcel.readString(); initialFailureTime = parcel.readLong(); pending = parcel.readInt() != 0; + initialize = parcel.readInt() != 0; } public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() { diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index f251984..fbdd5ae 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -346,16 +346,6 @@ public class SyncStorageEngine extends Handler { } } } - // Inform the backup manager about a data change - IBackupManager ibm = IBackupManager.Stub.asInterface( - ServiceManager.getService(Context.BACKUP_SERVICE)); - if (ibm != null) { - try { - ibm.dataChanged("com.android.providers.settings"); - } catch (RemoteException e) { - // Try again later - } - } } public boolean getSyncAutomatically(Account account, String providerName) { @@ -521,6 +511,9 @@ public class SyncStorageEngine extends Handler { SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); status.pending = true; + status.initialize = op.extras != null && + op.extras.containsKey(ContentResolver.SYNC_EXTRAS_INITIALIZE) && + op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE); } reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index 50faf57..11c67cc 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -274,6 +274,25 @@ public class CompatibilityInfo { * Apply translation to the canvas that is necessary to draw the content. */ public void translateCanvas(Canvas canvas) { + if (applicationScale == 1.5f) { + /* When we scale for compatibility, we can put our stretched + bitmaps and ninepatches on exacty 1/2 pixel boundaries, + which can give us inconsistent drawing due to imperfect + float precision in the graphics engine's inverse matrix. + + As a work-around, we translate by a tiny amount to avoid + landing on exact pixel centers and boundaries, giving us + the slop we need to draw consistently. + + This constant is meant to resolve to 1/255 after it is + scaled by 1.5 (applicationScale). Note, this is just a guess + as to what is small enough not to create its own artifacts, + and big enough to avoid the precision problems. Feel free + to experiment with smaller values as you choose. + */ + final float tinyOffset = 2.0f / (3 * 255); + canvas.translate(tinyOffset, tinyOffset); + } canvas.scale(applicationScale, applicationScale); } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 13effe0..5f1a3c5 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -137,7 +137,6 @@ public class Camera { * * @throws IOException if the method fails. * - * FIXME: Unhide after approval * @hide */ public native final void reconnect() throws IOException; @@ -150,25 +149,20 @@ public class Camera { * Camera object is locked. Locking it again from the same process will * have no effect. Attempting to lock it from another process if it has * not been unlocked will fail. - * Returns 0 if lock was successful. * - * FIXME: Unhide after approval - * @hide + * @throws RuntimeException if the method fails. */ - public native final int lock(); + public native final void lock(); /** * Unlock the camera to allow another process to access it. To save * setup/teardown time, a client of Camera can pass an initialized Camera * object to another process. This method is used to unlock the Camera * object before handing off the Camera object to the other process. - - * Returns 0 if unlock was successful. * - * FIXME: Unhide after approval - * @hide + * @throws RuntimeException if the method fails. */ - public native final int unlock(); + public native final void unlock(); /** * Sets the SurfaceHolder to be used for a picture preview. If the surface @@ -363,10 +357,12 @@ public class Camera { /** * Starts auto-focus function and registers a callback function to run when - * camera is focused. Only valid after startPreview() has been called. If - * the camera does not support auto-focus, it is a no-op and {@link - * AutoFocusCallback#onAutoFocus(boolean, Camera)} callback will be called - * immediately. + * camera is focused. Only valid after startPreview() has been called. + * Applications should call {@link + * android.hardware.Camera.Parameters#getFocusMode()} to determine if this + * method should be called. If the camera does not support auto-focus, it is + * a no-op and {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} + * callback will be called immediately. * <p>If your application should not be installed * on devices without auto-focus, you must declare that your application * uses auto-focus with the @@ -604,6 +600,7 @@ public class Camera { private static final String KEY_ANTIBANDING = "antibanding"; private static final String KEY_SCENE_MODE = "scene-mode"; private static final String KEY_FLASH_MODE = "flash-mode"; + private static final String KEY_FOCUS_MODE = "focus-mode"; // Parameter key suffix for supported values. private static final String SUPPORTED_VALUES_SUFFIX = "-values"; @@ -652,6 +649,10 @@ public class Camera { * Flash will be fired in red-eye reduction mode. */ public static final String FLASH_MODE_RED_EYE = "red-eye"; + /** + * Constant emission of light. This can be used for video recording. + */ + public static final String FLASH_MODE_VIDEO_LIGHT = "video-light"; // Values for scene mode settings. public static final String SCENE_MODE_AUTO = "auto"; @@ -670,9 +671,29 @@ public class Camera { public static final String SCENE_MODE_PARTY = "party"; public static final String SCENE_MODE_CANDLELIGHT = "candlelight"; + // Values for focus mode settings. + /** + * Auto-focus mode. + */ + public static final String FOCUS_MODE_AUTO = "auto"; + /** + * Focus is set at infinity. Applications should not call + * {@link #autoFocus(AutoFocusCallback)} in this mode. + */ + public static final String FOCUS_MODE_INFINITY = "infinity"; + public static final String FOCUS_MODE_MACRO = "macro"; + /** + * Focus is fixed. The camera is always in this mode if the focus is not + * adjustable. If the camera has auto-focus, this mode can fix the + * focus, which is usually at hyperfocal distance. Applications should + * not call {@link #autoFocus(AutoFocusCallback)} in this mode. + */ + public static final String FOCUS_MODE_FIXED = "fixed"; + // Formats for setPreviewFormat and setPictureFormat. private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp"; private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp"; + private static final String PIXEL_FORMAT_YUV422I = "yuv422i-yuyv"; private static final String PIXEL_FORMAT_RGB565 = "rgb565"; private static final String PIXEL_FORMAT_JPEG = "jpeg"; @@ -957,7 +978,13 @@ public class Camera { */ public List<Integer> getSupportedPreviewFormats() { String str = get(KEY_PREVIEW_FORMAT + SUPPORTED_VALUES_SUFFIX); - return splitInt(str); + ArrayList<Integer> formats = new ArrayList<Integer>(); + for (String s : split(str)) { + int f = pixelFormatForCameraFormat(s); + if (f == PixelFormat.UNKNOWN) continue; + formats.add(f); + } + return formats; } /** @@ -1036,6 +1063,7 @@ public class Camera { switch(pixel_format) { case PixelFormat.YCbCr_422_SP: return PIXEL_FORMAT_YUV422SP; case PixelFormat.YCbCr_420_SP: return PIXEL_FORMAT_YUV420SP; + case PixelFormat.YCbCr_422_I: return PIXEL_FORMAT_YUV422I; case PixelFormat.RGB_565: return PIXEL_FORMAT_RGB565; case PixelFormat.JPEG: return PIXEL_FORMAT_JPEG; default: return null; @@ -1052,6 +1080,9 @@ public class Camera { if (format.equals(PIXEL_FORMAT_YUV420SP)) return PixelFormat.YCbCr_420_SP; + if (format.equals(PIXEL_FORMAT_YUV422I)) + return PixelFormat.YCbCr_422_I; + if (format.equals(PIXEL_FORMAT_RGB565)) return PixelFormat.RGB_565; @@ -1290,6 +1321,39 @@ public class Camera { return split(str); } + /** + * Gets the current focus mode setting. + * + * @return one of FOCUS_MODE_XXX string constant. If the camera does not + * support auto-focus, this should return {@link + * #FOCUS_MODE_FIXED}. If the focus mode is not FOCUS_MODE_FIXED + * or {@link #FOCUS_MODE_INFINITY}, applications should call + * {@link #autoFocus(AutoFocusCallback)} to start the focus. + */ + public String getFocusMode() { + return get(KEY_FOCUS_MODE); + } + + /** + * Sets the focus mode. + * + * @param value FOCUS_MODE_XXX string constants. + */ + public void setFocusMode(String value) { + set(KEY_FOCUS_MODE, value); + } + + /** + * Gets the supported focus modes. + * + * @return a List of FOCUS_MODE_XXX string constants. null if focus mode + * setting is not supported. + */ + public List<String> getSupportedFocusModes() { + String str = get(KEY_FOCUS_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + // Splits a comma delimited string to an ArrayList of String. // Return null if the passing string is null or the size is 0. private ArrayList<String> split(String str) { diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 8f1a756..44b73c5 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -18,10 +18,73 @@ package android.os; /** * The BatteryManager class contains strings and constants used for values - * in the ACTION_BATTERY_CHANGED Intent. + * in the {@link android.content.Intent#ACTION_BATTERY_CHANGED} Intent. */ public class BatteryManager { - + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the current status constant. + */ + public static final String EXTRA_STATUS = "status"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the current health constant. + */ + public static final String EXTRA_HEALTH = "health"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * boolean indicating whether a battery is present. + */ + public static final String EXTRA_PRESENT = "present"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer field containing the current battery level, from 0 to + * {@link #EXTRA_SCALE}. + */ + public static final String EXTRA_LEVEL = "level"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the maximum battery level. + */ + public static final String EXTRA_SCALE = "scale"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the resource ID of a small status bar icon + * indicating the current battery state. + */ + public static final String EXTRA_ICON_SMALL = "icon-small"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer indicating whether the device is plugged in to a power + * source; 0 means it is on battery, other constants are different + * types of power sources. + */ + public static final String EXTRA_PLUGGED = "plugged"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the current battery voltage level. + */ + public static final String EXTRA_VOLTAGE = "voltage"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the current battery temperature. + */ + public static final String EXTRA_TEMPERATURE = "temperature"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * String describing the technology of the current battery. + */ + public static final String EXTRA_TECHNOLOGY = "technology"; + // values for "status" field in the ACTION_BATTERY_CHANGED Intent public static final int BATTERY_STATUS_UNKNOWN = 1; public static final int BATTERY_STATUS_CHARGING = 2; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index e203fd5..a49a27a0 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -314,6 +314,15 @@ public abstract class BatteryStats implements Parcelable { * @return foreground cpu time in microseconds */ public abstract long getForegroundTime(int which); + + /** + * Returns the approximate cpu time spent in microseconds, at a certain CPU speed. + * @param speedStep the index of the CPU speed. This is not the actual speed of the + * CPU. + * @param which one of STATS_TOTAL, STATS_LAST, STATS_CURRENT or STATS_UNPLUGGED + * @see BatteryStats#getCpuSpeedSteps() + */ + public abstract long getTimeAtCpuSpeedStep(int speedStep, int which); } /** @@ -573,6 +582,9 @@ public abstract class BatteryStats implements Parcelable { public abstract Map<String, ? extends Timer> getKernelWakelockStats(); + /** Returns the number of different speeds that the CPU can run at */ + public abstract int getCpuSpeedSteps(); + private final static void formatTimeRaw(StringBuilder out, long seconds) { long days = seconds / (60 * 60 * 24); if (days != 0) { diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java index b337d28..a264594 100644 --- a/core/java/android/preference/VolumePreference.java +++ b/core/java/android/preference/VolumePreference.java @@ -121,6 +121,9 @@ public class VolumePreference extends SeekBarPreference implements if (mSeekBarVolumizer != null) { Dialog dialog = getDialog(); if (dialog != null && dialog.isShowing()) { + View view = dialog.getWindow().getDecorView() + .findViewById(com.android.internal.R.id.seekbar); + if (view != null) view.setOnKeyListener(null); // Stopped while dialog was showing, revert changes mSeekBarVolumizer.revertVolume(); } diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index 1de971b..f046cef 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -110,6 +110,7 @@ public final class Calendar { public static final int EDITOR_ACCESS = 600; /** Full access to the calendar */ public static final int OWNER_ACCESS = 700; + /** Domain admin */ public static final int ROOT_ACCESS = 800; /** diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 93ee3ba..3df228d 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -410,7 +410,8 @@ public final class ContactsContract { private AggregationSuggestions() {} /** - * The directory twig for this sub-table + * The directory twig for this sub-table. The URI can be followed by an optional + * type-to-filter, similar to {@link Contacts#CONTENT_FILTER_URI}. */ public static final String CONTENT_DIRECTORY = "suggestions"; } @@ -677,12 +678,20 @@ public final class ContactsContract { } /** + * Combines all columns returned by {@link Data} table queries. + */ + private interface DataColumnsWithJoins extends BaseColumns, DataColumns, RawContactsColumns, + ContactsColumns, ContactOptionsColumns { + + } + + /** * Constants for the data table, which contains data points tied to a raw contact. * For example, a phone number or email address. Each row in this table contains a type * definition and some generic columns. Each data type can define the meaning for each of * the generic columns. */ - public static final class Data implements BaseColumns, DataColumns { + public final static class Data implements DataColumnsWithJoins { /** * This utility class cannot be instantiated */ @@ -892,32 +901,9 @@ public final class ContactsContract { public static final String PACKAGE_COMMON = "common"; /** - * Columns common across the specific types. - */ - private interface BaseCommonColumns { - /** - * The package name to use when creating {@link Resources} objects for - * this data row. This value is only designed for use when building user - * interfaces, and should not be used to infer the owner. - */ - public static final String RES_PACKAGE = "res_package"; - - /** - * The MIME type of the item represented by this row. - */ - public static final String MIMETYPE = "mimetype"; - - /** - * The {@link RawContacts#_ID} that this data belongs to. - */ - public static final String RAW_CONTACT_ID = "raw_contact_id"; - } - - /** * The base types that all "Typed" data kinds support. */ public interface BaseTypes { - /** * A custom type. The custom label should be supplied by user. */ @@ -927,96 +913,96 @@ public final class ContactsContract { /** * Columns common across the specific types. */ - private interface CommonColumns extends BaseTypes{ + private interface CommonColumns extends BaseTypes { /** - * The type of data, for example Home or Work. - * <P>Type: INTEGER</P> + * The data for the contact method. + * <P>Type: TEXT</P> */ - public static final String TYPE = "data1"; + public static final String DATA = DataColumns.DATA1; /** - * The data for the contact method. - * <P>Type: TEXT</P> + * The type of data, for example Home or Work. + * <P>Type: INTEGER</P> */ - public static final String DATA = "data2"; + public static final String TYPE = DataColumns.DATA2; /** * The user defined label for the the contact method. * <P>Type: TEXT</P> */ - public static final String LABEL = "data3"; + public static final String LABEL = DataColumns.DATA3; } /** * Parts of the name. */ - public static final class StructuredName implements BaseCommonColumns { + public static final class StructuredName implements DataColumnsWithJoins { private StructuredName() {} /** MIME type used when storing this in data table. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/name"; /** + * The name that should be used to display the contact. + * <i>Unstructured component of the name should be consistent with + * its structured representation.</i> + * <p> + * Type: TEXT + */ + public static final String DISPLAY_NAME = DATA1; + + /** * The given name for the contact. * <P>Type: TEXT</P> */ - public static final String GIVEN_NAME = "data1"; + public static final String GIVEN_NAME = DATA2; /** * The family name for the contact. * <P>Type: TEXT</P> */ - public static final String FAMILY_NAME = "data2"; + public static final String FAMILY_NAME = DATA3; /** * The contact's honorific prefix, e.g. "Sir" * <P>Type: TEXT</P> */ - public static final String PREFIX = "data3"; + public static final String PREFIX = DATA4; /** * The contact's middle name * <P>Type: TEXT</P> */ - public static final String MIDDLE_NAME = "data4"; + public static final String MIDDLE_NAME = DATA5; /** * The contact's honorific suffix, e.g. "Jr" */ - public static final String SUFFIX = "data5"; + public static final String SUFFIX = DATA6; /** * The phonetic version of the given name for the contact. * <P>Type: TEXT</P> */ - public static final String PHONETIC_GIVEN_NAME = "data6"; + public static final String PHONETIC_GIVEN_NAME = DATA7; /** * The phonetic version of the additional name for the contact. * <P>Type: TEXT</P> */ - public static final String PHONETIC_MIDDLE_NAME = "data7"; + public static final String PHONETIC_MIDDLE_NAME = DATA8; /** * The phonetic version of the family name for the contact. * <P>Type: TEXT</P> */ - public static final String PHONETIC_FAMILY_NAME = "data8"; - - /** - * The name that should be used to display the contact. - * <i>Unstructured component of the name should be consistent with - * its structured representation.</i> - * <p> - * Type: TEXT - */ - public static final String DISPLAY_NAME = "data9"; + public static final String PHONETIC_FAMILY_NAME = DATA9; } /** * A nickname. */ - public static final class Nickname implements CommonColumns, BaseCommonColumns { + public static final class Nickname implements DataColumnsWithJoins, CommonColumns { private Nickname() {} /** MIME type used when storing this in data table. */ @@ -1037,7 +1023,7 @@ public final class ContactsContract { /** * Common data definition for telephone numbers. */ - public static final class Phone implements BaseCommonColumns, CommonColumns { + public static final class Phone implements DataColumnsWithJoins, CommonColumns { private Phone() {} /** MIME type used when storing this in data table. */ @@ -1093,43 +1079,85 @@ public final class ContactsContract { */ public static final String NUMBER = DATA; + /** + * @deprecated use {@link #getTypeLabel(Resources, int, CharSequence)} instead. + */ + @Deprecated public static final CharSequence getDisplayLabel(Context context, int type, CharSequence label, CharSequence[] labelArray) { - CharSequence display = ""; - - if (type != Phone.TYPE_CUSTOM) { - CharSequence[] labels = labelArray != null? labelArray - : context.getResources().getTextArray( - com.android.internal.R.array.phoneTypes); - try { - display = labels[type - 1]; - } catch (ArrayIndexOutOfBoundsException e) { - display = labels[Phone.TYPE_CUSTOM]; - } - } else { - if (!TextUtils.isEmpty(label)) { - display = label; - } - } - return display; + return getTypeLabel(context.getResources(), type, label); } + /** + * @deprecated use {@link #getTypeLabel(Resources, int, CharSequence)} instead. + */ + @Deprecated public static final CharSequence getDisplayLabel(Context context, int type, CharSequence label) { - return getDisplayLabel(context, type, label, null); + return getTypeLabel(context.getResources(), type, label); + } + + /** + * Return the string resource that best describes the given + * {@link CommonColumns#TYPE}. Will always return a valid resource. + */ + public static final int getTypeLabelResource(int type) { + switch (type) { + case TYPE_HOME: return com.android.internal.R.string.phoneTypeHome; + case TYPE_MOBILE: return com.android.internal.R.string.phoneTypeMobile; + case TYPE_WORK: return com.android.internal.R.string.phoneTypeWork; + case TYPE_FAX_WORK: return com.android.internal.R.string.phoneTypeFaxWork; + case TYPE_FAX_HOME: return com.android.internal.R.string.phoneTypeFaxHome; + case TYPE_PAGER: return com.android.internal.R.string.phoneTypePager; + case TYPE_OTHER: return com.android.internal.R.string.phoneTypeOther; + case TYPE_CALLBACK: return com.android.internal.R.string.phoneTypeCallback; + case TYPE_CAR: return com.android.internal.R.string.phoneTypeCar; + case TYPE_COMPANY_MAIN: return com.android.internal.R.string.phoneTypeCompanyMain; + case TYPE_ISDN: return com.android.internal.R.string.phoneTypeIsdn; + case TYPE_MAIN: return com.android.internal.R.string.phoneTypeMain; + case TYPE_OTHER_FAX: return com.android.internal.R.string.phoneTypeOtherFax; + case TYPE_RADIO: return com.android.internal.R.string.phoneTypeRadio; + case TYPE_TELEX: return com.android.internal.R.string.phoneTypeTelex; + case TYPE_TTY_TDD: return com.android.internal.R.string.phoneTypeTtyTdd; + case TYPE_WORK_MOBILE: return com.android.internal.R.string.phoneTypeWorkMobile; + case TYPE_WORK_PAGER: return com.android.internal.R.string.phoneTypeWorkPager; + case TYPE_ASSISTANT: return com.android.internal.R.string.phoneTypeAssistant; + case TYPE_MMS: return com.android.internal.R.string.phoneTypeMms; + default: return com.android.internal.R.string.phoneTypeCustom; + } + } + + /** + * Return a {@link CharSequence} that best describes the given type, + * possibly substituting the given {@link CommonColumns#LABEL} value + * for {@link BaseTypes#TYPE_CUSTOM}. + */ + public static final CharSequence getTypeLabel(Resources res, int type, + CharSequence label) { + if ((type == TYPE_CUSTOM || type == TYPE_ASSISTANT) && !TextUtils.isEmpty(label)) { + return label; + } else { + final int labelRes = getTypeLabelResource(type); + return res.getText(labelRes); + } } } /** * Common data definition for email addresses. */ - public static final class Email implements BaseCommonColumns, CommonColumns { + public static final class Email implements DataColumnsWithJoins, CommonColumns { private Email() {} /** MIME type used when storing this in data table. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email_v2"; /** + * The MIME type of {@link #CONTENT_URI} providing a directory of email addresses. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/email_v2"; + + /** * The content:// style URI for all data records of the * {@link Email#CONTENT_ITEM_TYPE} MIME type, combined with the * associated raw contact and aggregate contact data. @@ -1145,9 +1173,6 @@ public final class ContactsContract { public static final Uri CONTENT_LOOKUP_URI = Uri.withAppendedPath(CONTENT_URI, "lookup"); - @Deprecated - public static final Uri CONTENT_FILTER_EMAIL_URI = CONTENT_LOOKUP_URI; - /** * The content:// style URL for email lookup using a filter. The filter returns * records of MIME type {@link Email#CONTENT_ITEM_TYPE}. The filter is applied @@ -1166,13 +1191,42 @@ public final class ContactsContract { * The display name for the email address * <P>Type: TEXT</P> */ - public static final String DISPLAY_NAME = "data4"; + public static final String DISPLAY_NAME = DATA4; + + /** + * Return the string resource that best describes the given + * {@link CommonColumns#TYPE}. Will always return a valid resource. + */ + public static final int getTypeLabelResource(int type) { + switch (type) { + case TYPE_HOME: return com.android.internal.R.string.emailTypeHome; + case TYPE_WORK: return com.android.internal.R.string.emailTypeWork; + case TYPE_OTHER: return com.android.internal.R.string.emailTypeOther; + case TYPE_MOBILE: return com.android.internal.R.string.emailTypeMobile; + default: return com.android.internal.R.string.emailTypeCustom; + } + } + + /** + * Return a {@link CharSequence} that best describes the given type, + * possibly substituting the given {@link CommonColumns#LABEL} value + * for {@link BaseTypes#TYPE_CUSTOM}. + */ + public static final CharSequence getTypeLabel(Resources res, int type, + CharSequence label) { + if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) { + return label; + } else { + final int labelRes = getTypeLabelResource(type); + return res.getText(labelRes); + } + } } /** * Common data definition for postal addresses. */ - public static final class StructuredPostal implements BaseCommonColumns, CommonColumns { + public static final class StructuredPostal implements DataColumnsWithJoins, CommonColumns { private StructuredPostal() { } @@ -1211,7 +1265,7 @@ public final class ContactsContract { * <p> * Type: TEXT */ - public static final String STREET = "data6"; + public static final String STREET = DATA4; /** * Covers actual P.O. boxes, drawers, locked bags, etc. This is @@ -1219,7 +1273,7 @@ public final class ContactsContract { * <p> * Type: TEXT */ - public static final String POBOX = "data7"; + public static final String POBOX = DATA5; /** * This is used to disambiguate a street address when a city @@ -1229,7 +1283,7 @@ public final class ContactsContract { * <p> * Type: TEXT */ - public static final String NEIGHBORHOOD = "data8"; + public static final String NEIGHBORHOOD = DATA6; /** * Can be city, village, town, borough, etc. This is the postal town @@ -1237,7 +1291,7 @@ public final class ContactsContract { * <p> * Type: TEXT */ - public static final String CITY = "data9"; + public static final String CITY = DATA7; /** * A state, province, county (in Ireland), Land (in Germany), @@ -1245,7 +1299,7 @@ public final class ContactsContract { * <p> * Type: TEXT */ - public static final String REGION = "data11"; + public static final String REGION = DATA8; /** * Postal code. Usually country-wide, but sometimes specific to the @@ -1253,20 +1307,48 @@ public final class ContactsContract { * <p> * Type: TEXT */ - public static final String POSTCODE = "data12"; + public static final String POSTCODE = DATA9; /** * The name or code of the country. * <p> * Type: TEXT */ - public static final String COUNTRY = "data13"; + public static final String COUNTRY = DATA10; + + /** + * Return the string resource that best describes the given + * {@link CommonColumns#TYPE}. Will always return a valid resource. + */ + public static final int getTypeLabelResource(int type) { + switch (type) { + case TYPE_HOME: return com.android.internal.R.string.postalTypeHome; + case TYPE_WORK: return com.android.internal.R.string.postalTypeWork; + case TYPE_OTHER: return com.android.internal.R.string.postalTypeOther; + default: return com.android.internal.R.string.postalTypeCustom; + } + } + + /** + * Return a {@link CharSequence} that best describes the given type, + * possibly substituting the given {@link CommonColumns#LABEL} value + * for {@link BaseTypes#TYPE_CUSTOM}. + */ + public static final CharSequence getTypeLabel(Resources res, int type, + CharSequence label) { + if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) { + return label; + } else { + final int labelRes = getTypeLabelResource(type); + return res.getText(labelRes); + } + } } /** * Common data definition for IM addresses. */ - public static final class Im implements BaseCommonColumns, CommonColumns { + public static final class Im implements DataColumnsWithJoins, CommonColumns { private Im() {} /** MIME type used when storing this in data table. */ @@ -1282,9 +1364,9 @@ public final class ContactsContract { * column is {@link #PROTOCOL_CUSTOM}, the {@link #CUSTOM_PROTOCOL} * should contain the name of the custom protocol. */ - public static final String PROTOCOL = "data5"; + public static final String PROTOCOL = DATA5; - public static final String CUSTOM_PROTOCOL = "data6"; + public static final String CUSTOM_PROTOCOL = DATA6; /* * The predefined IM protocol types. @@ -1299,12 +1381,74 @@ public final class ContactsContract { public static final int PROTOCOL_ICQ = 6; public static final int PROTOCOL_JABBER = 7; public static final int PROTOCOL_NETMEETING = 8; + + /** + * Return the string resource that best describes the given + * {@link CommonColumns#TYPE}. Will always return a valid resource. + */ + public static final int getTypeLabelResource(int type) { + switch (type) { + case TYPE_HOME: return com.android.internal.R.string.imTypeHome; + case TYPE_WORK: return com.android.internal.R.string.imTypeWork; + case TYPE_OTHER: return com.android.internal.R.string.imTypeOther; + default: return com.android.internal.R.string.imTypeCustom; + } + } + + /** + * Return a {@link CharSequence} that best describes the given type, + * possibly substituting the given {@link CommonColumns#LABEL} value + * for {@link BaseTypes#TYPE_CUSTOM}. + */ + public static final CharSequence getTypeLabel(Resources res, int type, + CharSequence label) { + if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) { + return label; + } else { + final int labelRes = getTypeLabelResource(type); + return res.getText(labelRes); + } + } + + /** + * Return the string resource that best describes the given + * {@link Im#PROTOCOL}. Will always return a valid resource. + */ + public static final int getProtocolLabelResource(int type) { + switch (type) { + case PROTOCOL_AIM: return com.android.internal.R.string.imProtocolAim; + case PROTOCOL_MSN: return com.android.internal.R.string.imProtocolMsn; + case PROTOCOL_YAHOO: return com.android.internal.R.string.imProtocolYahoo; + case PROTOCOL_SKYPE: return com.android.internal.R.string.imProtocolSkype; + case PROTOCOL_QQ: return com.android.internal.R.string.imProtocolQq; + case PROTOCOL_GOOGLE_TALK: return com.android.internal.R.string.imProtocolGoogleTalk; + case PROTOCOL_ICQ: return com.android.internal.R.string.imProtocolIcq; + case PROTOCOL_JABBER: return com.android.internal.R.string.imProtocolJabber; + case PROTOCOL_NETMEETING: return com.android.internal.R.string.imProtocolNetMeeting; + default: return com.android.internal.R.string.imProtocolCustom; + } + } + + /** + * Return a {@link CharSequence} that best describes the given + * protocol, possibly substituting the given + * {@link #CUSTOM_PROTOCOL} value for {@link #PROTOCOL_CUSTOM}. + */ + public static final CharSequence getProtocolLabel(Resources res, int type, + CharSequence label) { + if (type == PROTOCOL_CUSTOM && !TextUtils.isEmpty(label)) { + return label; + } else { + final int labelRes = getProtocolLabelResource(type); + return res.getText(labelRes); + } + } } /** * Common data definition for organizations. */ - public static final class Organization implements BaseCommonColumns, CommonColumns { + public static final class Organization implements DataColumnsWithJoins, CommonColumns { private Organization() {} /** MIME type used when storing this in data table. */ @@ -1323,37 +1467,64 @@ public final class ContactsContract { * The position title at this company as the user entered it. * <P>Type: TEXT</P> */ - public static final String TITLE = "data4"; + public static final String TITLE = DATA4; /** * The department at this company as the user entered it. * <P>Type: TEXT</P> */ - public static final String DEPARTMENT = "data5"; + public static final String DEPARTMENT = DATA5; /** * The job description at this company as the user entered it. * <P>Type: TEXT</P> */ - public static final String JOB_DESCRIPTION = "data6"; + public static final String JOB_DESCRIPTION = DATA6; /** * The symbol of this company as the user entered it. * <P>Type: TEXT</P> */ - public static final String SYMBOL = "data7"; + public static final String SYMBOL = DATA7; /** * The phonetic name of this company as the user entered it. * <P>Type: TEXT</P> */ - public static final String PHONETIC_NAME = "data8"; + public static final String PHONETIC_NAME = DATA8; + + /** + * Return the string resource that best describes the given + * {@link CommonColumns#TYPE}. Will always return a valid resource. + */ + public static final int getTypeLabelResource(int type) { + switch (type) { + case TYPE_WORK: return com.android.internal.R.string.orgTypeWork; + case TYPE_OTHER: return com.android.internal.R.string.orgTypeOther; + default: return com.android.internal.R.string.orgTypeCustom; + } + } + + /** + * Return a {@link CharSequence} that best describes the given type, + * possibly substituting the given {@link CommonColumns#LABEL} value + * for {@link BaseTypes#TYPE_CUSTOM}. + */ + public static final CharSequence getTypeLabel(Resources res, int type, + CharSequence label) { + if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) { + return label; + } else { + final int labelRes = getTypeLabelResource(type); + return res.getText(labelRes); + } + } } /** * Common data definition for miscellaneous information. */ - public static final class Miscellaneous implements BaseCommonColumns { + public static final class Miscellaneous implements DataColumnsWithJoins { private Miscellaneous() {} /** MIME type used when storing this in data table. */ @@ -1363,19 +1534,19 @@ public final class ContactsContract { * The birthday as the user entered it. * <P>Type: TEXT</P> */ - public static final String BIRTHDAY = "data1"; + public static final String BIRTHDAY = DATA1; /** * The nickname as the user entered it. * <P>Type: TEXT</P> */ - public static final String NICKNAME = "data2"; + public static final String NICKNAME = DATA2; } /** * Common data definition for relations. */ - public static final class Relation implements BaseCommonColumns, CommonColumns { + public static final class Relation implements DataColumnsWithJoins, CommonColumns { private Relation() {} /** MIME type used when storing this in data table. */ @@ -1406,7 +1577,7 @@ public final class ContactsContract { /** * Common data definition for events. */ - public static final class Event implements BaseCommonColumns, CommonColumns { + public static final class Event implements DataColumnsWithJoins, CommonColumns { private Event() {} /** MIME type used when storing this in data table. */ @@ -1425,7 +1596,7 @@ public final class ContactsContract { /** * Photo of the contact. */ - public static final class Photo implements BaseCommonColumns { + public static final class Photo implements DataColumnsWithJoins { private Photo() {} /** MIME type used when storing this in data table. */ @@ -1437,13 +1608,13 @@ public final class ContactsContract { * <p> * Type: BLOB */ - public static final String PHOTO = "data1"; + public static final String PHOTO = DATA15; } /** * Notes about the contact. */ - public static final class Note implements BaseCommonColumns { + public static final class Note implements DataColumnsWithJoins { private Note() {} /** MIME type used when storing this in data table. */ @@ -1453,13 +1624,13 @@ public final class ContactsContract { * The note text. * <P>Type: TEXT</P> */ - public static final String NOTE = "data1"; + public static final String NOTE = DATA1; } /** * Group Membership. */ - public static final class GroupMembership implements BaseCommonColumns { + public static final class GroupMembership implements DataColumnsWithJoins { private GroupMembership() {} /** MIME type used when storing this in data table. */ @@ -1471,7 +1642,7 @@ public final class ContactsContract { * this or {@link #GROUP_SOURCE_ID} must be set when inserting a row. * <P>Type: INTEGER</P> */ - public static final String GROUP_ROW_ID = "data1"; + public static final String GROUP_ROW_ID = DATA1; /** * The sourceid of the group that this group membership refers to. Exactly one of @@ -1484,7 +1655,7 @@ public final class ContactsContract { /** * Website related to the contact. */ - public static final class Website implements BaseCommonColumns, CommonColumns { + public static final class Website implements DataColumnsWithJoins, CommonColumns { private Website() {} /** MIME type used when storing this in data table. */ @@ -1502,7 +1673,7 @@ public final class ContactsContract { * The website URL string. * <P>Type: TEXT</P> */ - public static final String URL = "data1"; + public static final String URL = DATA; } } @@ -1672,24 +1843,12 @@ public final class ContactsContract { */ public static final int TYPE_KEEP_TOGETHER = 1; - @Deprecated - public static final int TYPE_KEEP_IN = 1; - /** * Makes sure that the specified raw contacts are NOT included in the same * aggregate contact. */ public static final int TYPE_KEEP_SEPARATE = 2; - @Deprecated - public static final int TYPE_KEEP_OUT = 2; - - @Deprecated - public static final String CONTACT_ID = "contact_id"; - - @Deprecated - public static final String RAW_CONTACT_ID = "raw_contact_id"; - /** * A reference to the {@link RawContacts#_ID} of the raw contact that the rule applies to. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1d41df2..6cf8db6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2406,7 +2406,6 @@ public final class Settings { WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, WIFI_NUM_ALLOWED_CHANNELS, WIFI_NUM_OPEN_NETWORKS_KEPT, - BACKGROUND_DATA, }; /** diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index ba53307..f3bc3a6 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -247,7 +247,6 @@ class BluetoothEventLoop { addDevice(address, properties); } } - mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING); return; } @@ -266,8 +265,8 @@ class BluetoothEventLoop { } String name = propValues[0]; if (name.equals("Name")) { - Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED); - intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]); + Intent intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); + intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, propValues[1]); mContext.sendBroadcast(intent, BLUETOOTH_PERM); mBluetoothService.setProperty(name, propValues[1]); } else if (name.equals("Pairable") || name.equals("Discoverable")) { @@ -330,6 +329,9 @@ class BluetoothEventLoop { Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null"); return; } + if (DBG) { + log("Device property changed:" + address + "property:" + name); + } BluetoothDevice device = mAdapter.getRemoteDevice(address); if (name.equals("Name")) { Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED); @@ -366,6 +368,11 @@ class BluetoothEventLoop { uuid = str.toString(); } mBluetoothService.setRemoteDeviceProperty(address, name, uuid); + + // UUIDs have changed, query remote service channel and update cache. + mBluetoothService.updateDeviceServiceChannelCache(address); + + mBluetoothService.sendUuidIntent(address); } else if (name.equals("Paired")) { if (propValues[1].equals("true")) { mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED); @@ -396,6 +403,9 @@ class BluetoothEventLoop { mBluetoothService.cancelPairingUserInput(address); return null; } + // Set state to BONDING, for incoming connections it will be set here. + // For outgoing connections, it gets set when call createBond. + mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING); return address; } @@ -453,8 +463,9 @@ class BluetoothEventLoop { String address = checkPairingRequestAndGetAddress(objectPath, nativeData); if (address == null) return; - if (mBluetoothService.getBondState().getBondState(address) == - BluetoothDevice.BOND_BONDING) { + String pendingOutgoingAddress = + mBluetoothService.getBondState().getPendingOutgoingBonding(); + if (address.equals(pendingOutgoingAddress)) { // we initiated the bonding BluetoothClass btClass = new BluetoothClass(mBluetoothService.getRemoteClass(address)); @@ -528,6 +539,24 @@ class BluetoothEventLoop { return; } + private void onDiscoverServicesResult(String deviceObjectPath, boolean result) { + String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); + // We don't parse the xml here, instead just query Bluez for the properties. + if (result) { + mBluetoothService.updateRemoteDevicePropertiesCache(address); + } + mBluetoothService.sendUuidIntent(address); + } + + private void onCreateDeviceResult(String address, boolean result) { + if (DBG) { + log("Result of onCreateDeviceResult:" + result); + } + if (!result) { + mBluetoothService.sendUuidIntent(address); + } + } + private void onRestartRequired() { if (mBluetoothService.isEnabled()) { Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " + diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index c0e4f34..e5b20bd 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.bluetooth.ParcelUuid; import android.content.BroadcastReceiver; @@ -76,10 +77,18 @@ public class BluetoothService extends IBluetooth.Stub { private static final int MESSAGE_REGISTER_SDP_RECORDS = 1; private static final int MESSAGE_FINISH_DISABLE = 2; + private static final int MESSAGE_UUID_INTENT = 3; + + // The timeout used to sent the UUIDs Intent + // This timeout should be greater than the page timeout + private static final int UUID_INTENT_DELAY = 6000; private final Map<String, String> mAdapterProperties; private final HashMap <String, Map<String, String>> mDeviceProperties; + private final HashMap <String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache; + private final ArrayList <String> mUuidIntentTracker; + static { classInitNative(); } @@ -104,6 +113,9 @@ public class BluetoothService extends IBluetooth.Stub { mIsDiscovering = false; mAdapterProperties = new HashMap<String, String>(); mDeviceProperties = new HashMap<String, Map<String,String>>(); + + mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>(); + mUuidIntentTracker = new ArrayList<String>(); registerForAirplaneMode(); } @@ -291,6 +303,11 @@ public class BluetoothService extends IBluetooth.Stub { case MESSAGE_FINISH_DISABLE: finishDisable(msg.arg1 != 0); break; + case MESSAGE_UUID_INTENT: + String address = (String)msg.obj; + if (address != null) + sendUuidIntent(address); + break; } } }; @@ -414,6 +431,18 @@ public class BluetoothService extends IBluetooth.Stub { new ArrayList<String>(Arrays.asList( "Motorola IHF1000", "i.TechBlueBAND", "X5 Stereo v1.3")); + // If this is an outgoing connection, store the address. + // There can be only 1 pending outgoing connection at a time, + private String mPendingOutgoingBonding; + + private synchronized void setPendingOutgoingBonding(String address) { + mPendingOutgoingBonding = address; + } + + public synchronized String getPendingOutgoingBonding() { + return mPendingOutgoingBonding; + } + public synchronized void loadBondState() { if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) { return; @@ -444,6 +473,15 @@ public class BluetoothService extends IBluetooth.Stub { if (oldState == state) { return; } + + // Check if this was an pending outgoing bonding. + // If yes, reset the state. + if (oldState == BluetoothDevice.BOND_BONDING) { + if (address.equals(mPendingOutgoingBonding)) { + mPendingOutgoingBonding = null; + } + } + if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" + reason + ")"); Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); @@ -766,8 +804,7 @@ public class BluetoothService extends IBluetooth.Stub { } address = address.toUpperCase(); - String[] bonding = mBondState.listInState(BluetoothDevice.BOND_BONDING); - if (bonding.length > 0 && !bonding[0].equals(address)) { + if (mBondState.getPendingOutgoingBonding() != null) { log("Ignoring createBond(): another device is bonding"); // a different device is currently bonding, fail return false; @@ -785,7 +822,9 @@ public class BluetoothService extends IBluetooth.Stub { return false; } + mBondState.setPendingOutgoingBonding(address); mBondState.setBondState(address, BluetoothDevice.BOND_BONDING); + return true; } @@ -845,16 +884,22 @@ public class BluetoothService extends IBluetooth.Stub { // Query for remote device properties, again. // We will need to reload the cache when we switch Bluetooth on / off // or if we crash. - String[] propValues = getRemoteDeviceProperties(address); - if (propValues != null) { - addRemoteDeviceProperties(address, propValues); + if (updateRemoteDevicePropertiesCache(address)) return getRemoteDeviceProperty(address, property); - } } Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address); return null; } + /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) { + String[] propValues = getRemoteDeviceProperties(address); + if (propValues != null) { + addRemoteDeviceProperties(address, propValues); + return true; + } + return false; + } + /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) { /* * We get a DeviceFound signal every time RSSI changes or name changes. @@ -889,6 +934,10 @@ public class BluetoothService extends IBluetooth.Stub { propertyValues.put(name, newValue); } mDeviceProperties.put(address, propertyValues); + + // We have added a new remote device or updated its properties. + // Also update the serviceChannel cache. + updateDeviceServiceChannelCache(address); } /* package */ void removeRemoteDeviceProperties(String address) { @@ -976,6 +1025,10 @@ public class BluetoothService extends IBluetooth.Stub { if (!BluetoothAdapter.checkBluetoothAddress(address)) { return null; } + return getUuidFromCache(address); + } + + private ParcelUuid[] getUuidFromCache(String address) { String value = getRemoteDeviceProperty(address, "UUIDs"); if (value == null) return null; @@ -990,6 +1043,36 @@ public class BluetoothService extends IBluetooth.Stub { return uuids; } + public synchronized boolean fetchRemoteUuidsWithSdp(String address) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!BluetoothAdapter.checkBluetoothAddress(address)) { + return false; + } + + if (mUuidIntentTracker.contains(address)) { + // An SDP query for this address is already in progress + return true; + } + + boolean ret; + if (getBondState(address) == BluetoothDevice.BOND_BONDED) { + String path = getObjectPathFromAddress(address); + if (path == null) return false; + + // Use an empty string for the UUID pattern + ret = discoverServicesNative(path, ""); + } else { + ret = createDeviceNative(address); + } + + mUuidIntentTracker.add(address); + + Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); + message.obj = address; + mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY); + return ret; + } + /** * Gets the rfcomm channel associated with the UUID. * @@ -997,14 +1080,23 @@ public class BluetoothService extends IBluetooth.Stub { * @param uuid ParcelUuid of the service attribute * * @return rfcomm channel associated with the service attribute + * -1 on error */ public int getRemoteServiceChannel(String address, ParcelUuid uuid) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (!BluetoothAdapter.checkBluetoothAddress(address)) { return BluetoothDevice.ERROR; } - return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid.toString(), - 0x0004); + // Check if we are recovering from a crash. + if (mDeviceProperties.isEmpty()) { + if (!updateRemoteDevicePropertiesCache(address)) + return -1; + } + + Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address); + if (value != null && value.containsKey(uuid)) + return value.get(uuid); + return -1; } public synchronized boolean setPin(String address, byte[] pin) { @@ -1083,6 +1175,27 @@ public class BluetoothService extends IBluetooth.Stub { return cancelPairingUserInputNative(address, data.intValue()); } + public void updateDeviceServiceChannelCache(String address) { + ParcelUuid[] deviceUuids = getRemoteUuids(address); + // We are storing the rfcomm channel numbers only for the uuids + // we are interested in. + int channel; + ParcelUuid[] interestedUuids = {BluetoothUuid.Handsfree, + BluetoothUuid.HSP, + BluetoothUuid.ObexObjectPush}; + + Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>(); + for (ParcelUuid uuid: interestedUuids) { + if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) { + channel = + getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid.toString(), + 0x0004); + value.put(uuid, channel); + } + } + mDeviceServiceChannelCache.put(address, value); + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -1121,6 +1234,18 @@ public class BluetoothService extends IBluetooth.Stub { Settings.System.AIRPLANE_MODE_ON, 0) == 1; } + /* Broadcast the Uuid intent */ + /*package*/ synchronized void sendUuidIntent(String address) { + ParcelUuid[] uuid = getUuidFromCache(address); + Intent intent = new Intent(BluetoothDevice.ACTION_UUID); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); + intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + + if (mUuidIntentTracker.contains(address)) + mUuidIntentTracker.remove(address); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n"); @@ -1284,4 +1409,7 @@ public class BluetoothService extends IBluetooth.Stub { private native boolean setPairingConfirmationNative(String address, boolean confirm, int nativeData); private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value); + private native boolean createDeviceNative(String address); + private native boolean discoverServicesNative(String objectPath, String pattern); + } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 367b26c..baaa3ce 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -32,6 +32,18 @@ public final class Formatter { * @return formated string with the number */ public static String formatFileSize(Context context, long number) { + return formatFileSize(context, number, false); + } + + /** + * Like {@link #formatFileSize}, but trying to generate shorter numbers + * (showing fewer digits of precisin). + */ + public static String formatShortFileSize(Context context, long number) { + return formatFileSize(context, number, true); + } + + private static String formatFileSize(Context context, long number, boolean shorter) { if (context == null) { return ""; } @@ -58,13 +70,24 @@ public final class Formatter { suffix = com.android.internal.R.string.petabyteShort; result = result / 1024; } - if (result < 100) { - String value = String.format("%.2f", result); - return context.getResources(). - getString(com.android.internal.R.string.fileSizeSuffix, - value, context.getString(suffix)); + String value; + if (result < 1) { + value = String.format("%.2f", result); + } else if (result < 10) { + if (shorter) { + value = String.format("%.1f", result); + } else { + value = String.format("%.2f", result); + } + } else if (result < 100) { + if (shorter) { + value = String.format("%.0f", result); + } else { + value = String.format("%.2f", result); + } + } else { + value = String.format("%.0f", result); } - String value = String.format("%.0f", result); return context.getResources(). getString(com.android.internal.R.string.fileSizeSuffix, value, context.getString(suffix)); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index f67c4aa..396e380 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -488,7 +488,10 @@ public interface WindowManager extends ViewManager { * is locked. This will let application windows take precedence over * key guard or any other lock screens. Can be used with * {@link #FLAG_KEEP_SCREEN_ON} to turn screen on and display windows - * directly before showing the key guard window + * directly before showing the key guard window. Can be used with + * {@link #FLAG_DISMISS_KEYGUARD} to automatically fully dismisss + * non-secure keyguards. This flag only applies to the top-most + * full-screen window. */ public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000; @@ -506,6 +509,19 @@ public interface WindowManager extends ViewManager { * up the device) to turn the screen on. */ public static final int FLAG_TURN_SCREEN_ON = 0x00200000; + /** Window flag: when set the window will cause the keyguard to + * be dismissed, only if it is not a secure lock keyguard. Because such + * a keyguard is not needed for security, it will never re-appear if + * the user navigates to another window (in contrast to + * {@link #FLAG_SHOW_WHEN_LOCKED}, which will only temporarily + * hide both secure and non-secure keyguards but ensure they reappear + * when the user moves to another UI that doesn't hide them). + * If the keyguard is currently active and is secure (requires an + * unlock pattern) than the user will still need to confirm it before + * seeing this window, unless {@link #FLAG_SHOW_WHEN_LOCKED} has + * also been set. */ + public static final int FLAG_DISMISS_KEYGUARD = 0x00400000; + /** Window flag: special flag to limit the size of the window to be * original size ([320x480] x density). Used to create window for applications * running under compatibility mode. diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 45ff27e..1923743 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -314,7 +314,9 @@ public interface WindowManagerPolicy { public boolean showLw(boolean doAnimation); } - /** No transition happening. */ + /** Not set up for a transition. */ + public final int TRANSIT_UNSET = 0; + /** No animation for transition. */ public final int TRANSIT_NONE = 0; /** Window has been added to the screen. */ public final int TRANSIT_ENTER = 1; @@ -600,11 +602,18 @@ public interface WindowManagerPolicy { * returned, all windows given to layoutWindow() <em>must</em> have had a * frame assigned. * - * @return Return true if layout state may have changed (so that another - * layout will be performed). + * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT} + * and {@link #FINISH_LAYOUT_REDO_CONFIG}. */ - public boolean finishLayoutLw(); + public int finishLayoutLw(); + /** Layout state may have changed (so another layout will be performed) */ + static final int FINISH_LAYOUT_REDO_LAYOUT = 0x0001; + /** Configuration state may have changed */ + static final int FINISH_LAYOUT_REDO_CONFIG = 0x0002; + /** Wallpaper may need to move */ + static final int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004; + /** * Called when animation of the windows is about to start. * diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index dbddb2e..e233a02 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -605,8 +605,8 @@ class BrowserFrame extends Handler { } // Called by JNI when an apple-touch-icon attribute was found. - private void didReceiveTouchIconUrl(String url) { - mCallbackProxy.onReceivedTouchIconUrl(url); + private void didReceiveTouchIconUrl(String url, boolean precomposed) { + mCallbackProxy.onReceivedTouchIconUrl(url, precomposed); } /** @@ -707,6 +707,10 @@ class BrowserFrame extends Handler { return value.string.toString(); } + private float density() { + return mContext.getResources().getDisplayMetrics().density; + } + //========================================================================== // native functions //========================================================================== diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index b051675..1ec769b 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -249,7 +249,7 @@ class CallbackProxy extends Handler { case RECEIVED_TOUCH_ICON_URL: if (mWebChromeClient != null) { mWebChromeClient.onReceivedTouchIconUrl(mWebView, - (String) msg.obj); + (String) msg.obj, msg.arg1 == 1); } break; @@ -1065,19 +1065,22 @@ class CallbackProxy extends Handler { sendMessage(obtainMessage(RECEIVED_ICON, icon)); } - /* package */ void onReceivedTouchIconUrl(String url) { + /* package */ void onReceivedTouchIconUrl(String url, boolean precomposed) { // We should have a current item but we do not want to crash so check // for null. WebHistoryItem i = mBackForwardList.getCurrentItem(); if (i != null) { - i.setTouchIconUrl(url); + if (precomposed || i.getTouchIconUrl() != null) { + i.setTouchIconUrl(url); + } } // Do an unsynchronized quick check to avoid posting if no callback has // been set. if (mWebChromeClient == null) { return; } - sendMessage(obtainMessage(RECEIVED_TOUCH_ICON_URL, url)); + sendMessage(obtainMessage(RECEIVED_TOUCH_ICON_URL, + precomposed ? 1 : 0, 0, url)); } public void onReceivedTitle(String title) { diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java index e985ccc..c0cac01 100755 --- a/core/java/android/webkit/GeolocationPermissions.java +++ b/core/java/android/webkit/GeolocationPermissions.java @@ -19,10 +19,9 @@ package android.webkit; import android.os.Handler; import android.os.Message; import android.util.Log; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; @@ -47,15 +46,13 @@ public final class GeolocationPermissions { private static GeolocationPermissions sInstance; private Handler mHandler; + private Handler mUIHandler; // Members used to transfer the origins and permissions between threads. private Set<String> mOrigins; private boolean mAllowed; private Set<String> mOriginsToClear; private Set<String> mOriginsToAllow; - private static Lock mLock = new ReentrantLock(); - private static boolean mUpdated; - private static Condition mUpdatedCondition = mLock.newCondition(); // Message ids static final int GET_ORIGINS = 0; @@ -64,6 +61,15 @@ public final class GeolocationPermissions { static final int ALLOW = 3; static final int CLEAR_ALL = 4; + // Message ids on the UI thread + static final int RETURN_ORIGINS = 0; + static final int RETURN_ALLOWED = 1; + + private static final String ORIGINS = "origins"; + private static final String ORIGIN = "origin"; + private static final String CALLBACK = "callback"; + private static final String ALLOWED = "allowed"; + /** * Gets the singleton instance of the class. */ @@ -75,22 +81,62 @@ public final class GeolocationPermissions { } /** + * Creates the UI message handler. Must be called on the UI thread. + */ + public void createUIHandler() { + if (mUIHandler == null) { + mUIHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + // Runs on the UI thread. + switch (msg.what) { + case RETURN_ORIGINS: { + Map values = (Map) msg.obj; + Set origins = (Set) values.get(ORIGINS); + ValueCallback<Set> callback = (ValueCallback<Set>) values.get(CALLBACK); + callback.onReceiveValue(origins); + } break; + case RETURN_ALLOWED: { + Map values = (Map) msg.obj; + Boolean allowed = (Boolean) values.get(ALLOWED); + ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK); + callback.onReceiveValue(allowed); + } break; + } + } + }; + } + } + + /** * Creates the message handler. Must be called on the WebKit thread. */ public void createHandler() { - mLock.lock(); if (mHandler == null) { mHandler = new Handler() { @Override public void handleMessage(Message msg) { // Runs on the WebKit thread. switch (msg.what) { - case GET_ORIGINS: + case GET_ORIGINS: { getOriginsImpl(); - break; - case GET_ALLOWED: - getAllowedImpl((String) msg.obj); - break; + ValueCallback callback = (ValueCallback) msg.obj; + Set origins = new HashSet(mOrigins); + Map values = new HashMap<String, Object>(); + values.put(CALLBACK, callback); + values.put(ORIGINS, origins); + postUIMessage(Message.obtain(null, RETURN_ORIGINS, values)); + } break; + case GET_ALLOWED: { + Map values = (Map) msg.obj; + String origin = (String) values.get(ORIGIN); + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + getAllowedImpl(origin); + Map retValues = new HashMap<String, Object>(); + retValues.put(CALLBACK, callback); + retValues.put(ALLOWED, new Boolean(mAllowed)); + postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues)); + } break; case CLEAR: nativeClear((String) msg.obj); break; @@ -115,7 +161,6 @@ public final class GeolocationPermissions { } } } - mLock.unlock(); } /** @@ -127,29 +172,31 @@ public final class GeolocationPermissions { } /** + * Utility function to send a message to the handler on the UI thread + */ + private void postUIMessage(Message msg) { + if (mUIHandler != null) { + mUIHandler.sendMessage(msg); + } + } + + /** * Gets the set of origins for which Geolocation permissions are stored. * Note that we represent the origins as strings. These are created using * WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules' * (Database, Geolocation etc) do so, it's safe to match up origins for the * purposes of displaying UI. */ - public Set getOrigins() { - // Called on the UI thread. - Set origins = null; - mLock.lock(); - try { - mUpdated = false; - postMessage(Message.obtain(null, GET_ORIGINS)); - while (!mUpdated) { - mUpdatedCondition.await(); + public void getOrigins(ValueCallback<Set> callback) { + if (callback != null) { + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + getOriginsImpl(); + Set origins = new HashSet(mOrigins); + callback.onReceiveValue(origins); + } else { + postMessage(Message.obtain(null, GET_ORIGINS, callback)); } - origins = mOrigins; - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting for update", e); - } finally { - mLock.unlock(); } - return origins; } /** @@ -157,33 +204,29 @@ public final class GeolocationPermissions { */ private void getOriginsImpl() { // Called on the WebKit thread. - mLock.lock(); mOrigins = nativeGetOrigins(); - mUpdated = true; - mUpdatedCondition.signal(); - mLock.unlock(); } /** * Gets the permission state for the specified origin. */ - public boolean getAllowed(String origin) { - // Called on the UI thread. - boolean allowed = false; - mLock.lock(); - try { - mUpdated = false; - postMessage(Message.obtain(null, GET_ALLOWED, origin)); - while (!mUpdated) { - mUpdatedCondition.await(); - } - allowed = mAllowed; - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting for update", e); - } finally { - mLock.unlock(); + public void getAllowed(String origin, ValueCallback<Boolean> callback) { + if (callback == null) { + return; + } + if (origin == null) { + callback.onReceiveValue(null); + return; + } + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + getAllowedImpl(origin); + callback.onReceiveValue(new Boolean(mAllowed)); + } else { + Map values = new HashMap<String, Object>(); + values.put(ORIGIN, origin); + values.put(CALLBACK, callback); + postMessage(Message.obtain(null, GET_ALLOWED, values)); } - return allowed; } /** @@ -191,11 +234,7 @@ public final class GeolocationPermissions { */ private void getAllowedImpl(String origin) { // Called on the WebKit thread. - mLock.lock(); mAllowed = nativeGetAllowed(origin); - mUpdated = true; - mUpdatedCondition.signal(); - mLock.unlock(); } /** @@ -205,7 +244,6 @@ public final class GeolocationPermissions { */ public void clear(String origin) { // Called on the UI thread. - mLock.lock(); if (mHandler == null) { if (mOriginsToClear == null) { mOriginsToClear = new HashSet<String>(); @@ -217,7 +255,6 @@ public final class GeolocationPermissions { } else { postMessage(Message.obtain(null, CLEAR, origin)); } - mLock.unlock(); } /** @@ -227,7 +264,6 @@ public final class GeolocationPermissions { */ public void allow(String origin) { // Called on the UI thread. - mLock.lock(); if (mHandler == null) { if (mOriginsToAllow == null) { mOriginsToAllow = new HashSet<String>(); @@ -239,7 +275,6 @@ public final class GeolocationPermissions { } else { postMessage(Message.obtain(null, ALLOW, origin)); } - mLock.unlock(); } /** diff --git a/core/java/android/webkit/GoogleLocationSettingManager.java b/core/java/android/webkit/GoogleLocationSettingManager.java index fe7fce6..1b6e77c 100644 --- a/core/java/android/webkit/GoogleLocationSettingManager.java +++ b/core/java/android/webkit/GoogleLocationSettingManager.java @@ -35,8 +35,6 @@ import java.util.HashSet; * @hide pending API council review */ class GoogleLocationSettingManager { - // The application context. - private Context mContext; // The observer used to listen to the system setting. private GoogleLocationSettingObserver mSettingObserver; @@ -48,6 +46,8 @@ class GoogleLocationSettingManager { // by the browser. private final static String LAST_READ_USE_LOCATION_FOR_SERVICES = "lastReadUseLocationForServices"; + // The Browser package name. + private static final String BROWSER_PACKAGE_NAME = "com.android.browser"; // The Google origins we consider. private static HashSet<String> sGoogleOrigins; static { @@ -57,38 +57,72 @@ class GoogleLocationSettingManager { sGoogleOrigins.add("http://www.google.co.uk"); } - GoogleLocationSettingManager(Context context) { - mContext = context; + private static GoogleLocationSettingManager sGoogleLocationSettingManager = null; + private static int sRefCount = 0; + + static GoogleLocationSettingManager getInstance() { + if (sGoogleLocationSettingManager == null) { + sGoogleLocationSettingManager = new GoogleLocationSettingManager(); + } + return sGoogleLocationSettingManager; } + private GoogleLocationSettingManager() {} + /** * Starts the manager. Checks whether the setting has changed and * installs an observer to listen for future changes. */ - public void start() { - maybeApplySetting(); - + public void start(Context context) { + // Are we running in the browser? + if (context == null || !BROWSER_PACKAGE_NAME.equals(context.getPackageName())) { + return; + } + // Increase the refCount + sRefCount++; + // Are we already registered? + if (mSettingObserver != null) { + return; + } + // Read and apply the settings if needed. + maybeApplySetting(context); + // Register to receive notifications when the system settings change. mSettingObserver = new GoogleLocationSettingObserver(); - mSettingObserver.observe(); + mSettingObserver.observe(context); } /** + * Stops the manager. + */ + public void stop() { + // Are we already registered? + if (mSettingObserver == null) { + return; + } + if (--sRefCount == 0) { + mSettingObserver.doNotObserve(); + mSettingObserver = null; + } + } + /** * Checks to see if the system setting has changed and if so, * updates the Geolocation permissions accordingly. + * @param the Application context */ - private void maybeApplySetting() { - int setting = getSystemSetting(); - if (settingChanged(setting)) { + private void maybeApplySetting(Context context) { + int setting = getSystemSetting(context); + if (settingChanged(setting, context)) { applySetting(setting); } } /** * Gets the current system setting for 'Use location for Google services'. + * @param the Application context * @return The system setting. */ - private int getSystemSetting() { - return Settings.Secure.getInt(mContext.getContentResolver(), + private int getSystemSetting(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.USE_LOCATION_FOR_SERVICES, sSystemSettingFalse); } @@ -97,12 +131,13 @@ class GoogleLocationSettingManager { * Determines whether the supplied setting has changed from the last * value read by the browser. * @param setting The setting. + * @param the Application context * @return Whether the setting has changed from the last value read * by the browser. */ - private boolean settingChanged(int setting) { + private boolean settingChanged(int setting, Context context) { SharedPreferences preferences = - PreferenceManager.getDefaultSharedPreferences(mContext); + PreferenceManager.getDefaultSharedPreferences(context); // Default to false. If the system setting is false the first time it is ever read by the // browser, there's nothing to do. int lastReadSetting = sSystemSettingFalse; @@ -137,20 +172,35 @@ class GoogleLocationSettingManager { * This class implements an observer to listen for changes to the * system setting. */ - class GoogleLocationSettingObserver extends ContentObserver { + private class GoogleLocationSettingObserver extends ContentObserver { + private Context mContext; + GoogleLocationSettingObserver() { super(new Handler()); } - void observe() { - ContentResolver resolver = mContext.getContentResolver(); + void observe(Context context) { + if (mContext != null) { + return; + } + ContentResolver resolver = context.getContentResolver(); resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.USE_LOCATION_FOR_SERVICES), false, this); + mContext = context; + } + + void doNotObserve() { + if (mContext == null) { + return; + } + ContentResolver resolver = mContext.getContentResolver(); + resolver.unregisterContentObserver(this); + mContext = null; } @Override public void onChange(boolean selfChange) { - maybeApplySetting(); + maybeApplySetting(mContext); } } } diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java index 32eea5f..766bd75 100644 --- a/core/java/android/webkit/PluginManager.java +++ b/core/java/android/webkit/PluginManager.java @@ -63,8 +63,11 @@ public class PluginManager { private final Context mContext; + private ArrayList<PackageInfo> mPackageInfoCache; + private PluginManager(Context context) { mContext = context; + mPackageInfoCache = new ArrayList<PackageInfo>(); } public static synchronized PluginManager getInstance(Context context) { @@ -92,65 +95,94 @@ public class PluginManager { } String[] getPluginDirectories() { + ArrayList<String> directories = new ArrayList<String>(); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> plugins = pm.queryIntentServices(new Intent( PLUGIN_ACTION), PackageManager.GET_SERVICES); - for (ResolveInfo info : plugins) { - ServiceInfo serviceInfo = info.serviceInfo; - if (serviceInfo == null) { - Log.w(LOGTAG, "Ignore bad plugin"); - continue; - } - PackageInfo pkgInfo; - try { - pkgInfo = pm.getPackageInfo(serviceInfo.packageName, - PackageManager.GET_PERMISSIONS - | PackageManager.GET_SIGNATURES); - } catch (NameNotFoundException e) { - Log.w(LOGTAG, "Cant find plugin: " + serviceInfo.packageName); - continue; - } - if (pkgInfo == null) { - continue; - } - String directory = pkgInfo.applicationInfo.dataDir + "/lib"; - if (directories.contains(directory)) { - continue; - } - String permissions[] = pkgInfo.requestedPermissions; - if (permissions == null) { - continue; - } - boolean permissionOk = false; - for (String permit : permissions) { - if (PLUGIN_PERMISSION.equals(permit)) { - permissionOk = true; + + synchronized(mPackageInfoCache) { + + // clear the list of existing packageInfo objects + mPackageInfoCache.clear(); + + for (ResolveInfo info : plugins) { + ServiceInfo serviceInfo = info.serviceInfo; + if (serviceInfo == null) { + Log.w(LOGTAG, "Ignore bad plugin"); + continue; + } + PackageInfo pkgInfo; + try { + pkgInfo = pm.getPackageInfo(serviceInfo.packageName, + PackageManager.GET_PERMISSIONS + | PackageManager.GET_SIGNATURES); + } catch (NameNotFoundException e) { + Log.w(LOGTAG, "Cant find plugin: " + serviceInfo.packageName); + continue; + } + if (pkgInfo == null) { + continue; + } + String directory = pkgInfo.applicationInfo.dataDir + "/lib"; + if (directories.contains(directory)) { + continue; + } + String permissions[] = pkgInfo.requestedPermissions; + if (permissions == null) { + continue; + } + boolean permissionOk = false; + for (String permit : permissions) { + if (PLUGIN_PERMISSION.equals(permit)) { + permissionOk = true; + break; + } + } + if (!permissionOk) { + continue; + } + Signature signatures[] = pkgInfo.signatures; + if (signatures == null) { + continue; + } + boolean signatureMatch = false; + for (Signature signature : signatures) { + // TODO: check signature against Google provided one + signatureMatch = true; break; } + if (!signatureMatch) { + continue; + } + mPackageInfoCache.add(pkgInfo); + directories.add(directory); } - if (!permissionOk) { - continue; - } - Signature signatures[] = pkgInfo.signatures; - if (signatures == null) { - continue; - } - boolean signatureMatch = false; - for (Signature signature : signatures) { - // TODO: check signature against Google provided one - signatureMatch = true; - break; - } - if (!signatureMatch) { - continue; - } - directories.add(directory); } return directories.toArray(new String[directories.size()]); } + String getPluginsAPKName(String pluginLib) { + + // basic error checking on input params + if (pluginLib == null || pluginLib.length() == 0) { + return null; + } + + // must be synchronized to ensure the consistency of the cache + synchronized(mPackageInfoCache) { + for (PackageInfo pkgInfo : mPackageInfoCache) { + if (pluginLib.startsWith(pkgInfo.applicationInfo.dataDir)) { + return pkgInfo.packageName; + } + } + } + + // if no apk was found then return null + return null; + } + String getPluginSharedDataDirectory() { return mContext.getDir("plugins", 0).getPath(); } diff --git a/core/java/android/webkit/ValueCallback.java b/core/java/android/webkit/ValueCallback.java new file mode 100644 index 0000000..d8c5cdc --- /dev/null +++ b/core/java/android/webkit/ValueCallback.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2009 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; + +/** + * A callback interface used to returns values asynchronously + * + * @hide pending council approval + */ +public interface ValueCallback<T> { + /** + * Invoked when we have the result + */ + public void onReceiveValue(T value); +}; diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 0e08514..1ae1d85 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -48,9 +48,11 @@ public class WebChromeClient { * Notify the host application of the url for an apple-touch-icon. * @param view The WebView that initiated the callback. * @param url The icon url. + * @param precomposed True if the url is for a precomposed touch icon. * @hide pending council approval */ - public void onReceivedTouchIconUrl(WebView view, String url) {} + public void onReceivedTouchIconUrl(WebView view, String url, + boolean precomposed) {} /** * A callback interface used by the host application to notify diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 196bbd7..0cfcb95 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -17,6 +17,7 @@ package android.webkit; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Build; import android.os.Handler; @@ -189,9 +190,11 @@ public class WebSettings { private boolean mAllowFileAccess = true; private boolean mLoadWithOverviewMode = false; - // Manages interaction of the system setting 'Location & security - Share - // with Google' and the browser. - static GoogleLocationSettingManager sGoogleLocationSettingManager; + // private WebSettings, not accessible by the host activity + static private int mDoubleTapToastCount = 3; + + private static final String PREF_FILE = "WebViewSettings"; + private static final String DOUBLE_TAP_TOAST_COUNT = "double_tap_toast_count"; // Class to handle messages before WebCore is ready. private class EventHandler { @@ -199,6 +202,8 @@ public class WebSettings { static final int SYNC = 0; // Message id for setting priority static final int PRIORITY = 1; + // Message id for writing double-tap toast count + static final int SET_DOUBLE_TAP_TOAST_COUNT = 2; // Actual WebCore thread handler private Handler mHandler; @@ -224,6 +229,16 @@ public class WebSettings { setRenderPriority(); break; } + + case SET_DOUBLE_TAP_TOAST_COUNT: { + SharedPreferences.Editor editor = mContext + .getSharedPreferences(PREF_FILE, + Context.MODE_PRIVATE).edit(); + editor.putInt(DOUBLE_TAP_TOAST_COUNT, + mDoubleTapToastCount); + editor.commit(); + break; + } } } }; @@ -1311,6 +1326,19 @@ public class WebSettings { } } + int getDoubleTapToastCount() { + return mDoubleTapToastCount; + } + + void setDoubleTapToastCount(int count) { + if (mDoubleTapToastCount != count) { + mDoubleTapToastCount = count; + // write the settings in the non-UI thread + mEventHandler.sendMessage(Message.obtain(null, + EventHandler.SET_DOUBLE_TAP_TOAST_COUNT)); + } + } + /** * Transfer messages from the queue to the new WebCoreThread. Called from * WebCore thread. @@ -1321,13 +1349,28 @@ public class WebSettings { if (DebugFlags.WEB_SETTINGS) { junit.framework.Assert.assertTrue(frame.mNativeFrame != 0); } - sGoogleLocationSettingManager = new GoogleLocationSettingManager(mContext); - sGoogleLocationSettingManager.start(); + + GoogleLocationSettingManager.getInstance().start(mContext); + + SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, + Context.MODE_PRIVATE); + if (mDoubleTapToastCount > 0) { + mDoubleTapToastCount = sp.getInt(DOUBLE_TAP_TOAST_COUNT, + mDoubleTapToastCount); + } nativeSync(frame.mNativeFrame); mSyncPending = false; mEventHandler.createHandler(); } + /** + * Let the Settings object know that our owner is being destroyed. + */ + /*package*/ + synchronized void onDestroyed() { + GoogleLocationSettingManager.getInstance().stop(); + } + private int pin(int size) { // FIXME: 72 is just an arbitrary max text size value. if (size < 1) { diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java index ae560fb..0022248 100644 --- a/core/java/android/webkit/WebStorage.java +++ b/core/java/android/webkit/WebStorage.java @@ -20,9 +20,8 @@ import android.os.Handler; import android.os.Message; import android.util.Log; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.Collection; +import java.util.Map; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -51,28 +50,41 @@ public final class WebStorage { // Global instance of a WebStorage private static WebStorage sWebStorage; - // We keep the origins, quotas and usages as member values - // that we protect via a lock and update in syncValues(). - // This is needed to transfer this data across threads. - private static Lock mLock = new ReentrantLock(); - private static Condition mUpdateCondition = mLock.newCondition(); - private static boolean mUpdateAvailable; - // Message ids static final int UPDATE = 0; static final int SET_QUOTA_ORIGIN = 1; static final int DELETE_ORIGIN = 2; static final int DELETE_ALL = 3; + static final int GET_ORIGINS = 4; + static final int GET_USAGE_ORIGIN = 5; + static final int GET_QUOTA_ORIGIN = 6; + + // Message ids on the UI thread + static final int RETURN_ORIGINS = 0; + static final int RETURN_USAGE_ORIGIN = 1; + static final int RETURN_QUOTA_ORIGIN = 2; + + private static final String ORIGINS = "origins"; + private static final String ORIGIN = "origin"; + private static final String CALLBACK = "callback"; + private static final String USAGE = "usage"; + private static final String QUOTA = "quota"; - private Set <String> mOrigins; - private HashMap <String, Long> mQuotas = new HashMap<String, Long>(); - private HashMap <String, Long> mUsages = new HashMap<String, Long>(); + private Map <String, Origin> mOrigins; private Handler mHandler = null; + private Handler mUIHandler = null; - private static class Origin { + static class Origin { String mOrigin = null; long mQuota = 0; + long mUsage = 0; + + public Origin(String origin, long quota, long usage) { + mOrigin = origin; + mQuota = quota; + mUsage = usage; + } public Origin(String origin, long quota) { mOrigin = origin; @@ -90,11 +102,49 @@ public final class WebStorage { public long getQuota() { return mQuota; } + + public long getUsage() { + return mUsage; + } + } + + /** + * @hide + * Message handler, UI side + */ + public void createUIHandler() { + if (mUIHandler == null) { + mUIHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case RETURN_ORIGINS: { + Map values = (Map) msg.obj; + Map origins = (Map) values.get(ORIGINS); + ValueCallback<Map> callback = (ValueCallback<Map>) values.get(CALLBACK); + callback.onReceiveValue(origins); + } break; + + case RETURN_USAGE_ORIGIN: { + Map values = (Map) msg.obj; + ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK); + callback.onReceiveValue((Long)values.get(USAGE)); + } break; + + case RETURN_QUOTA_ORIGIN: { + Map values = (Map) msg.obj; + ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK); + callback.onReceiveValue((Long)values.get(QUOTA)); + } break; + } + } + }; + } } /** * @hide - * Message handler + * Message handler, webcore side */ public void createHandler() { if (mHandler == null) { @@ -117,6 +167,46 @@ public final class WebStorage { nativeDeleteAllData(); break; + case GET_ORIGINS: { + syncValues(); + ValueCallback callback = (ValueCallback) msg.obj; + Map origins = new HashMap(mOrigins); + Map values = new HashMap<String, Object>(); + values.put(CALLBACK, callback); + values.put(ORIGINS, origins); + postUIMessage(Message.obtain(null, RETURN_ORIGINS, values)); + } break; + + case GET_USAGE_ORIGIN: { + syncValues(); + Map values = (Map) msg.obj; + String origin = (String) values.get(ORIGIN); + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + Origin website = mOrigins.get(origin); + Map retValues = new HashMap<String, Object>(); + retValues.put(CALLBACK, callback); + if (website != null) { + long usage = website.getUsage(); + retValues.put(USAGE, new Long(usage)); + } + postUIMessage(Message.obtain(null, RETURN_USAGE_ORIGIN, retValues)); + } break; + + case GET_QUOTA_ORIGIN: { + syncValues(); + Map values = (Map) msg.obj; + String origin = (String) values.get(ORIGIN); + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + Origin website = mOrigins.get(origin); + Map retValues = new HashMap<String, Object>(); + retValues.put(CALLBACK, callback); + if (website != null) { + long quota = website.getQuota(); + retValues.put(QUOTA, new Long(quota)); + } + postUIMessage(Message.obtain(null, RETURN_QUOTA_ORIGIN, retValues)); + } break; + case UPDATE: syncValues(); break; @@ -126,82 +216,91 @@ public final class WebStorage { } } + /* + * When calling getOrigins(), getUsageForOrigin() and getQuotaForOrigin(), + * we need to get the values from webcore, but we cannot block while doing so + * as we used to do, as this could result in a full deadlock (other webcore + * messages received while we are still blocked here, see http://b/2127737). + * + * We have to do everything asynchronously, by providing a callback function. + * We post a message on the webcore thread (mHandler) that will get the result + * from webcore, and we post it back on the UI thread (using mUIHandler). + * We can then use the callback function to return the value. + */ + /** * @hide * Returns a list of origins having a database */ - public Set getOrigins() { - Set ret = null; - mLock.lock(); - try { - mUpdateAvailable = false; - update(); - while (!mUpdateAvailable) { - mUpdateCondition.await(); + public void getOrigins(ValueCallback<Map> callback) { + if (callback != null) { + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + callback.onReceiveValue(mOrigins); + } else { + postMessage(Message.obtain(null, GET_ORIGINS, callback)); } - ret = mOrigins; - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting on the updated origins", e); - } finally { - mLock.unlock(); } - return ret; + } + + /** + * Returns a list of origins having a database + * should only be called from WebViewCore. + */ + Collection<Origin> getOriginsSync() { + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + update(); + return mOrigins.values(); + } + return null; } /** * @hide * Returns the use for a given origin */ - public long getUsageForOrigin(String origin) { - long ret = 0; + public void getUsageForOrigin(String origin, ValueCallback<Long> callback) { + if (callback == null) { + return; + } if (origin == null) { - return ret; + callback.onReceiveValue(null); + return; } - mLock.lock(); - try { - mUpdateAvailable = false; - update(); - while (!mUpdateAvailable) { - mUpdateCondition.await(); - } - Long usage = mUsages.get(origin); - if (usage != null) { - ret = usage.longValue(); - } - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting on the updated origins", e); - } finally { - mLock.unlock(); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + Origin website = mOrigins.get(origin); + callback.onReceiveValue(new Long(website.getUsage())); + } else { + HashMap values = new HashMap<String, Object>(); + values.put(ORIGIN, origin); + values.put(CALLBACK, callback); + postMessage(Message.obtain(null, GET_USAGE_ORIGIN, values)); } - return ret; } /** * @hide * Returns the quota for a given origin */ - public long getQuotaForOrigin(String origin) { - long ret = 0; + public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) { + if (callback == null) { + return; + } if (origin == null) { - return ret; + callback.onReceiveValue(null); + return; } - mLock.lock(); - try { - mUpdateAvailable = false; - update(); - while (!mUpdateAvailable) { - mUpdateCondition.await(); - } - Long quota = mQuotas.get(origin); - if (quota != null) { - ret = quota.longValue(); - } - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting on the updated origins", e); - } finally { - mLock.unlock(); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + Origin website = mOrigins.get(origin); + callback.onReceiveValue(new Long(website.getUsage())); + } else { + HashMap values = new HashMap<String, Object>(); + values.put(ORIGIN, origin); + values.put(CALLBACK, callback); + postMessage(Message.obtain(null, GET_QUOTA_ORIGIN, values)); } - return ret; } /** @@ -256,6 +355,15 @@ public final class WebStorage { } /** + * Utility function to send a message to the handler on the UI thread + */ + private void postUIMessage(Message msg) { + if (mUIHandler != null) { + mUIHandler.sendMessage(msg); + } + } + + /** * @hide * Get the global instance of WebStorage. * @return A single instance of WebStorage. @@ -284,21 +392,14 @@ public final class WebStorage { * set the local values with the current ones */ private void syncValues() { - mLock.lock(); - Set tmp = nativeGetOrigins(); - mOrigins = new HashSet<String>(); - mQuotas.clear(); - mUsages.clear(); - Iterator<String> iter = tmp.iterator(); - while (iter.hasNext()) { - String origin = iter.next(); - mOrigins.add(origin); - mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin))); - mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin))); + Set<String> tmp = nativeGetOrigins(); + mOrigins = new HashMap<String, Origin>(); + for (String origin : tmp) { + Origin website = new Origin(origin, + nativeGetUsageForOrigin(origin), + nativeGetQuotaForOrigin(origin)); + mOrigins.put(origin, website); } - mUpdateAvailable = true; - mUpdateCondition.signal(); - mLock.unlock(); } // Native functions diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 39a2470..1a65ce8 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -448,8 +448,13 @@ import java.util.ArrayList; int initialScrollX = Touch.getInitialScrollX(this, buffer); int initialScrollY = Touch.getInitialScrollY(this, buffer); super.onTouchEvent(event); - if (Math.abs(mScrollX - initialScrollX) > slop - || Math.abs(mScrollY - initialScrollY) > slop) { + int dx = Math.abs(mScrollX - initialScrollX); + int dy = Math.abs(mScrollY - initialScrollY); + // Use a smaller slop when checking to see if we've moved far enough + // to scroll the text, because experimentally, slop has shown to be + // to big for the case of a small textfield. + int smallerSlop = slop/2; + if (dx > smallerSlop || dy > smallerSlop) { if (mWebView != null) { mWebView.scrollFocusedTextInput(mScrollX, mScrollY); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 7e8ba8f..51c5e1f 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -420,6 +420,7 @@ public class WebView extends AbsoluteLayout private static final int STD_SPEED = 480; // pixels per second // time for the longest scroll animation private static final int MAX_DURATION = 750; // milliseconds + private static final int SLIDE_TITLE_DURATION = 500; // milliseconds private Scroller mScroller; private boolean mWrapContent; @@ -1781,12 +1782,22 @@ public class WebView extends AbsoluteLayout } /** + * Given a distance in view space, convert it to content space. Note: this + * does not reflect translation, just scaling, so this should not be called + * with coordinates, but should be called for dimensions like width or + * height. + */ + private int viewToContentDimension(int d) { + return Math.round(d * mInvActualScale); + } + + /** * Given an x coordinate in view space, convert it to content space. Also * may be used for absolute heights (such as for the WebTextView's * textSize, which is unaffected by the height of the title bar). */ /*package*/ int viewToContentX(int x) { - return Math.round(x * mInvActualScale); + return viewToContentDimension(x); } /** @@ -1795,7 +1806,7 @@ public class WebView extends AbsoluteLayout * embedded into the WebView. */ /*package*/ int viewToContentY(int y) { - return viewToContentX(y - getTitleHeight()); + return viewToContentDimension(y - getTitleHeight()); } /** @@ -1810,7 +1821,7 @@ public class WebView extends AbsoluteLayout /** * Given an x coordinate in content space, convert it to view - * space. Also used for absolute heights. + * space. */ /*package*/ int contentToViewX(int x) { return contentToViewDimension(x); @@ -2000,7 +2011,7 @@ public class WebView extends AbsoluteLayout getGlobalVisibleRect(r, p); r.offset(-p.x, -p.y); if (mFindIsUp) { - r.bottom -= FIND_HEIGHT; + r.bottom -= mFindHeight; } } @@ -2072,9 +2083,6 @@ public class WebView extends AbsoluteLayout } } - // Make sure this stays in sync with the actual height of the FindDialog. - private static final int FIND_HEIGHT = 79; - @Override protected int computeVerticalScrollRange() { if (mDrawHistory) { @@ -2305,7 +2313,11 @@ public class WebView extends AbsoluteLayout * that were found. */ public int findAll(String find) { - mFindIsUp = true; + if (mFindIsUp == false) { + recordNewContentSize(mContentWidth, mContentHeight + mFindHeight, + false); + mFindIsUp = true; + } int result = nativeFindAll(find.toLowerCase(), find.toUpperCase()); invalidate(); return result; @@ -2314,6 +2326,7 @@ public class WebView extends AbsoluteLayout // Used to know whether the find dialog is open. Affects whether // or not we draw the highlights for matches. private boolean mFindIsUp; + private int mFindHeight; /** * Return the first substring consisting of the address of a physical @@ -2369,7 +2382,11 @@ public class WebView extends AbsoluteLayout * Clear the highlighting surrounding text matches created by findAll. */ public void clearMatches() { - mFindIsUp = false; + if (mFindIsUp) { + recordNewContentSize(mContentWidth, mContentHeight - mFindHeight, + false); + mFindIsUp = false; + } nativeSetFindIsDown(); // Now that the dialog has been removed, ensure that we scroll to a // location that is not beyond the end of the page. @@ -2378,6 +2395,16 @@ public class WebView extends AbsoluteLayout } /** + * @hide + */ + public void setFindDialogHeight(int height) { + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "setFindDialogHeight height=" + height); + } + mFindHeight = height; + } + + /** * Query the document to see if it contains any image references. The * message object will be dispatched with arg1 being set to 1 if images * were found and 0 if the document does not reference any images. @@ -2419,7 +2446,6 @@ public class WebView extends AbsoluteLayout private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) { return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration); } - // helper to pin the scrollTo parameters (already in view coordinates) // returns true if the scroll was changed private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) { @@ -2431,15 +2457,6 @@ public class WebView extends AbsoluteLayout if ((dx | dy) == 0) { return false; } - // By this point we have added in the title bar's height. If the site - // is trying to scroll to the top of the page, scroll it to the top - // of the WebView including showing the title bar. - // mobile sites prefer to scroll to (0, 1), thus the + 1 below - if (getVisibleTitleHeight() > 0 && x == 0 - && y <= getTitleHeight() + 1) { - y = 0; - animate = false; - } if (animate) { // Log.d(LOGTAG, "startScroll: " + dx + " " + dy); mScroller.startScroll(mScrollX, mScrollY, dx, dy, @@ -2498,10 +2515,31 @@ public class WebView extends AbsoluteLayout // saved scroll position, it is ok to skip this. return false; } - int vx = contentToViewX(cx); - int vy = contentToViewY(cy); + int vx; + int vy; + if ((cx | cy) == 0) { + // If the page is being scrolled to (0,0), do not add in the title + // bar's height, and simply scroll to (0,0). (The only other work + // in contentToView_ is to multiply, so this would not change 0.) + vx = 0; + vy = 0; + } else { + vx = contentToViewX(cx); + vy = contentToViewY(cy); + } // Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" + // vx + " " + vy + "]"); + // Some mobile sites attempt to scroll the title bar off the page by + // scrolling to (0,1). If we are at the top left corner of the + // page, assume this is an attempt to scroll off the title bar, and + // animate the title bar off screen slowly enough that the user can see + // it. + if (cx == 0 && cy == 1 && mScrollX == 0 && mScrollY == 0) { + pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION); + // Since we are animating, we have not yet reached the desired + // scroll position. Do not return true to request another attempt + return false; + } pinScrollTo(vx, vy, false, 0); // If the request was to scroll to a negative coordinate, treat it as if // it was a request to scroll to 0 @@ -3732,6 +3770,13 @@ public class WebView extends AbsoluteLayout && !mZoomButtonsController.isVisible() && mMinZoomScale < mMaxZoomScale) { mZoomButtonsController.setVisible(true); + int count = settings.getDoubleTapToastCount(); + if (mInZoomOverview && count > 0) { + settings.setDoubleTapToastCount(--count); + Toast.makeText(mContext, + com.android.internal.R.string.double_tap_toast, + Toast.LENGTH_LONG).show(); + } } } @@ -4410,7 +4455,10 @@ public class WebView extends AbsoluteLayout return; } mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, viewToContentX(x), - viewToContentY(y)); + // Since this position is relative to the top of the text input + // field, we do not need to take the title bar's height into + // consideration. + viewToContentDimension(y)); } /** @@ -4504,7 +4552,8 @@ public class WebView extends AbsoluteLayout mZoomCenterY = mLastTouchY; mInZoomOverview = !mInZoomOverview; // remove the zoom control after double tap - if (getSettings().getBuiltInZoomControls()) { + WebSettings settings = getSettings(); + if (settings.getBuiltInZoomControls()) { if (mZoomButtonsController.isVisible()) { mZoomButtonsController.setVisible(false); } @@ -4516,7 +4565,10 @@ public class WebView extends AbsoluteLayout mZoomControls.hide(); } } + settings.setDoubleTapToastCount(0); if (mInZoomOverview) { + // Force the titlebar fully reveal in overview mode + if (mScrollY < getTitleHeight()) mScrollY = 0; zoomWithPreview((float) getViewWidth() / mZoomOverviewWidth); } else { // mLastTouchX and mLastTouchY are the point in the current viewport @@ -4816,7 +4868,7 @@ public class WebView extends AbsoluteLayout / draw.mMinPrefWidth; mMinZoomScaleFixed = false; } else { - mMinZoomScale = mDefaultScale; + mMinZoomScale = restoreState.mDefaultScale; mMinZoomScaleFixed = true; } } else { @@ -4833,22 +4885,13 @@ public class WebView extends AbsoluteLayout mMaxZoomScale = restoreState.mMaxScale; } setNewZoomScale(mLastScale, false); - if (getTitleHeight() != 0 && restoreState.mScrollX == 0 - && restoreState.mScrollY == 0) { - // If there is a title bar, and the page is being - // restored to (0,0), do not scroll the title bar - // off the page. - abortAnimation(); - scrollTo(0,0); - } else { - setContentScrollTo(restoreState.mScrollX, - restoreState.mScrollY); - } + setContentScrollTo(restoreState.mScrollX, + restoreState.mScrollY); if (useWideViewport && settings.getLoadWithOverviewMode()) { if (restoreState.mViewScale == 0 || (restoreState.mMobileSite - && mMinZoomScale < mDefaultScale)) { + && mMinZoomScale < restoreState.mDefaultScale)) { mInZoomOverview = true; } } @@ -4865,7 +4908,8 @@ public class WebView extends AbsoluteLayout final boolean updateLayout = viewSize.x == mLastWidthSent && viewSize.y == mLastHeightSent; recordNewContentSize(draw.mWidthHeight.x, - draw.mWidthHeight.y, updateLayout); + draw.mWidthHeight.y + + (mFindIsUp ? mFindHeight : 0), updateLayout); if (DebugFlags.WEB_VIEW) { Rect b = draw.mInvalRegion.getBounds(); Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" + diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index d4142bb..ce45373 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -17,6 +17,7 @@ package android.webkit; import android.content.Context; +import android.content.Intent; import android.graphics.Canvas; import android.graphics.DrawFilter; import android.graphics.Paint; @@ -37,6 +38,7 @@ import android.view.SurfaceView; import android.view.View; import java.util.ArrayList; +import java.util.Collection; import java.util.Map; import java.util.Set; @@ -100,6 +102,15 @@ final class WebViewCore { private boolean mViewportUserScalable = true; + /* + * range is from 70 to 400. + * 0 is a special value means device-dpi. The default scale factor will be + * always 100. + * -1 means undefined. The default scale factor will be + * WebView.DEFAULT_SCALE_PERCENT. + */ + private int mViewportDensityDpi = -1; + private int mRestoredScale = 0; private int mRestoredScreenWidthScale = 0; private int mRestoredX = 0; @@ -153,8 +164,10 @@ final class WebViewCore { // The WebIconDatabase needs to be initialized within the UI thread so // just request the instance here. WebIconDatabase.getInstance(); - // Create the WebStorage singleton - WebStorage.getInstance(); + // Create the WebStorage singleton and the UI handler + WebStorage.getInstance().createUIHandler(); + // Create the UI handler for GeolocationPermissions + GeolocationPermissions.getInstance().createUIHandler(); // Send a message to initialize the WebViewCore. Message init = sWebCoreHandler.obtainMessage( WebCoreThread.INITIALIZE, this); @@ -841,6 +854,7 @@ final class WebViewCore { synchronized (WebViewCore.this) { mBrowserFrame.destroy(); mBrowserFrame = null; + mSettings.onDestroyed(); mNativeClass = 0; } break; @@ -1510,13 +1524,14 @@ final class WebViewCore { // callbacks. Computes the sum of database quota for all origins. private long getUsedQuota() { WebStorage webStorage = WebStorage.getInstance(); - Set<String> origins = webStorage.getOrigins(); + Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); + if (origins == null) { return 0; } long usedQuota = 0; - for (String origin : origins) { - usedQuota += webStorage.getQuotaForOrigin(origin); + for (WebStorage.Origin website : origins) { + usedQuota += website.getQuota(); } return usedQuota; } @@ -1539,6 +1554,7 @@ final class WebViewCore { float mMaxScale; float mViewScale; float mTextWrapScale; + float mDefaultScale; int mScrollX; int mScrollY; boolean mMobileSite; @@ -1842,47 +1858,48 @@ final class WebViewCore { // set the viewport settings from WebKit setViewportSettingsFromNative(); - // adjust the default scale to match the density - if (WebView.DEFAULT_SCALE_PERCENT != 100) { - float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f; - if (mViewportInitialScale > 0) { - mViewportInitialScale *= adjust; - } - if (mViewportMinimumScale > 0) { - mViewportMinimumScale *= adjust; - } - if (mViewportMaximumScale > 0) { - mViewportMaximumScale *= adjust; + // adjust the default scale to match the densityDpi + float adjust = 1.0f; + if (mViewportDensityDpi == -1) { + if (WebView.DEFAULT_SCALE_PERCENT != 100) { + adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f; } + } else if (mViewportDensityDpi > 0) { + adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi + / mViewportDensityDpi; + } + int defaultScale = (int) (adjust * 100); + + if (mViewportInitialScale > 0) { + mViewportInitialScale *= adjust; + } + if (mViewportMinimumScale > 0) { + mViewportMinimumScale *= adjust; + } + if (mViewportMaximumScale > 0) { + mViewportMaximumScale *= adjust; } // infer the values if they are not defined. if (mViewportWidth == 0) { if (mViewportInitialScale == 0) { - mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; + mViewportInitialScale = defaultScale; } } if (mViewportUserScalable == false) { - mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; - mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT; - mViewportMaximumScale = WebView.DEFAULT_SCALE_PERCENT; + mViewportInitialScale = defaultScale; + mViewportMinimumScale = defaultScale; + mViewportMaximumScale = defaultScale; } - if (mViewportMinimumScale > mViewportInitialScale) { - if (mViewportInitialScale == 0) { - mViewportInitialScale = mViewportMinimumScale; - } else { - mViewportMinimumScale = mViewportInitialScale; - } + if (mViewportMinimumScale > mViewportInitialScale + && mViewportInitialScale != 0) { + mViewportMinimumScale = mViewportInitialScale; } - if (mViewportMaximumScale > 0) { - if (mViewportMaximumScale < mViewportInitialScale) { - mViewportMaximumScale = mViewportInitialScale; - } else if (mViewportInitialScale == 0) { - mViewportInitialScale = mViewportMaximumScale; - } + if (mViewportMaximumScale > 0 + && mViewportMaximumScale < mViewportInitialScale) { + mViewportMaximumScale = mViewportInitialScale; } - if (mViewportWidth < 0 - && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) { + if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) { mViewportWidth = 0; } @@ -1899,7 +1916,7 @@ final class WebViewCore { // we call WebView method from WebCore thread. But not perfect // reference is better than no reference. webViewWidth = mWebView.getViewWidth(); - viewportWidth = webViewWidth * 100 / WebView.DEFAULT_SCALE_PERCENT; + viewportWidth = (int) (webViewWidth / adjust); if (viewportWidth == 0) { Log.w(LOGTAG, "Can't get the viewWidth after the first layout"); } @@ -1909,6 +1926,7 @@ final class WebViewCore { mRestoreState = new RestoreState(); mRestoreState.mMinScale = mViewportMinimumScale / 100.0f; mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f; + mRestoreState.mDefaultScale = adjust; mRestoreState.mScrollX = mRestoredX; mRestoreState.mScrollY = mRestoredY; mRestoreState.mMobileSite = (0 == mViewportWidth); @@ -1930,8 +1948,7 @@ final class WebViewCore { mRestoreState.mViewScale = mRestoreState.mTextWrapScale = (float) webViewWidth / mViewportWidth; } else { - mRestoreState.mTextWrapScale = - WebView.DEFAULT_SCALE_PERCENT / 100.0f; + mRestoreState.mTextWrapScale = adjust; // 0 will trigger WebView to turn on zoom overview mode mRestoreState.mViewScale = 0; } @@ -2075,22 +2092,49 @@ final class WebViewCore { } } - // PluginWidget functions for creating SurfaceViews for the Surface drawing - // model. - private ViewManager.ChildView createSurface(String packageName, String className, + // called by JNI. PluginWidget function to launch an activity and overlays + // the activity with the View provided by the plugin class. + private void startFullScreenPluginActivity(String libName, String clsName, int npp) { + if (mWebView == null) { + return; + } + + String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName); + if (pkgName == null) { + Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); + return; + } + + Intent intent = new Intent("android.intent.webkit.PLUGIN"); + intent.putExtra(PluginActivity.INTENT_EXTRA_PACKAGE_NAME, pkgName); + intent.putExtra(PluginActivity.INTENT_EXTRA_CLASS_NAME, clsName); + intent.putExtra(PluginActivity.INTENT_EXTRA_NPP_INSTANCE, npp); + mWebView.getContext().startActivity(intent); + } + + // called by JNI. PluginWidget functions for creating an embedded View for + // the surface drawing model. + private ViewManager.ChildView createSurface(String libName, String clsName, int npp, int x, int y, int width, int height) { if (mWebView == null) { return null; } - PluginStub stub = PluginUtil.getPluginStub(mWebView.getContext(), packageName, className); + + String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName); + if (pkgName == null) { + Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); + return null; + } + + PluginStub stub =PluginUtil.getPluginStub(mWebView.getContext(),pkgName, clsName); if (stub == null) { - Log.e(LOGTAG, "Unable to find plugin class (" + className + - ") in the apk (" + packageName + ")"); + Log.e(LOGTAG, "Unable to find plugin class (" + clsName + + ") in the apk (" + pkgName + ")"); return null; } - + View pluginView = stub.getEmbeddedView(npp, mWebView.getContext()); - + ViewManager.ChildView view = mWebView.mViewManager.createView(); view.mView = pluginView; view.attachView(x, y, width, height); diff --git a/core/java/android/widget/FasttrackBadgeWidget.java b/core/java/android/widget/FasttrackBadgeWidget.java index 9d2307f..8c8e054 100644 --- a/core/java/android/widget/FasttrackBadgeWidget.java +++ b/core/java/android/widget/FasttrackBadgeWidget.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.database.Cursor; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.FastTrack; @@ -46,6 +47,7 @@ public class FasttrackBadgeWidget extends ImageView implements OnClickListener { private String mContactPhone; private int mMode; private QueryHandler mQueryHandler; + private Drawable mBadgeBackground; protected String[] mExcludeMimes = null; @@ -91,6 +93,8 @@ public class FasttrackBadgeWidget extends ImageView implements OnClickListener { a.recycle(); init(); + + mBadgeBackground = getBackground(); } private void init() { @@ -108,6 +112,17 @@ public class FasttrackBadgeWidget extends ImageView implements OnClickListener { */ public void assignContactUri(Uri contactUri) { mContactUri = contactUri; + mContactEmail = null; + mContactPhone = null; + onContactUriChanged(); + } + + private void onContactUriChanged() { + if (mContactUri == null && mContactEmail == null && mContactPhone == null) { + setBackgroundDrawable(null); + } else { + setBackgroundDrawable(mBadgeBackground); + } } /** @@ -127,6 +142,7 @@ public class FasttrackBadgeWidget extends ImageView implements OnClickListener { EMAIL_LOOKUP_PROJECTION, null, null, null); } else { mContactUri = null; + onContactUriChanged(); } } @@ -147,6 +163,7 @@ public class FasttrackBadgeWidget extends ImageView implements OnClickListener { PHONE_LOOKUP_PROJECTION, null, null, null); } else { mContactUri = null; + onContactUriChanged(); } } @@ -237,6 +254,7 @@ public class FasttrackBadgeWidget extends ImageView implements OnClickListener { } mContactUri = lookupUri; + onContactUriChanged(); if (trigger && lookupUri != null) { // Found contact, so trigger track diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 703cd8e..31c7814 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -743,7 +743,7 @@ public class ScrollView extends FrameLayout { final int maxJump = getMaxScrollAmount(); - if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump)) { + if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) { nextFocused.getDrawingRect(mTempRect); offsetDescendantRectToMyCoords(nextFocused, mTempRect); int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); @@ -792,19 +792,19 @@ public class ScrollView extends FrameLayout { * screen. */ private boolean isOffScreen(View descendant) { - return !isWithinDeltaOfScreen(descendant, 0); + return !isWithinDeltaOfScreen(descendant, 0, getHeight()); } /** * @return whether the descendant of this scroll view is within delta * pixels of being on the screen. */ - private boolean isWithinDeltaOfScreen(View descendant, int delta) { + private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) { descendant.getDrawingRect(mTempRect); offsetDescendantRectToMyCoords(descendant, mTempRect); return (mTempRect.bottom + delta) >= getScrollY() - && (mTempRect.top - delta) <= (getScrollY() + getHeight()); + && (mTempRect.top - delta) <= (getScrollY() + height); } /** @@ -1124,9 +1124,10 @@ public class ScrollView extends FrameLayout { if (null == currentFocused || this == currentFocused) return; - final int maxJump = mBottom - mTop; - - if (isWithinDeltaOfScreen(currentFocused, maxJump)) { + // If the currently-focused view was visible on the screen when the + // screen was at the old height, then scroll the screen to make that + // view visible with the new screen height. + if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) { currentFocused.getDrawingRect(mTempRect); offsetDescendantRectToMyCoords(currentFocused, mTempRect); int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 2e3364b..3c61c1c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2874,26 +2874,23 @@ 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); setInputType(type, false); - final int variation = type&(EditorInfo.TYPE_MASK_CLASS - |EditorInfo.TYPE_MASK_VARIATION); - final boolean isPassword = variation - == (EditorInfo.TYPE_CLASS_TEXT - |EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + final boolean isPassword = isPasswordInputType(type); + final boolean isVisiblePassword = isVisiblePasswordInputType(type); boolean forceUpdate = false; if (isPassword) { setTransformationMethod(PasswordTransformationMethod.getInstance()); setTypefaceByIndex(MONOSPACE, 0); - } else if (mTransformation == PasswordTransformationMethod.getInstance()) { - // We need to clean up if we were previously in password mode. - if (variation != (EditorInfo.TYPE_CLASS_TEXT - |EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)) { - setTypefaceByIndex(-1, -1); - } - forceUpdate = true; - } else if (variation == (EditorInfo.TYPE_CLASS_TEXT - |EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)) { + } else if (isVisiblePassword) { setTypefaceByIndex(MONOSPACE, 0); + } else if (wasPassword || wasVisiblePassword) { + // not in password mode, clean up typeface and transformation + setTypefaceByIndex(-1, -1); + if (mTransformation == PasswordTransformationMethod.getInstance()) { + forceUpdate = true; + } } boolean multiLine = (type&(EditorInfo.TYPE_MASK_CLASS @@ -2913,6 +2910,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null) imm.restartInput(this); } + private boolean isPasswordInputType(int inputType) { + final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS + | EditorInfo.TYPE_MASK_VARIATION); + return variation + == (EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + } + + private boolean isVisiblePasswordInputType(int inputType) { + final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS + | EditorInfo.TYPE_MASK_VARIATION); + return variation + == (EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + } + /** * Directly change the content type integer of the text view, without * modifying any other state. diff --git a/core/java/com/android/internal/backup/BackupConstants.java b/core/java/com/android/internal/backup/BackupConstants.java new file mode 100644 index 0000000..3ee11bd --- /dev/null +++ b/core/java/com/android/internal/backup/BackupConstants.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2009 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.internal.backup; + +/** + * Constants used internally between the backup manager and its transports + */ +public class BackupConstants { + public static final int TRANSPORT_OK = 0; + public static final int TRANSPORT_ERROR = 1; + public static final int TRANSPORT_NOT_INITIALIZED = 2; +} diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index af06965..a830ebd 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -22,24 +22,6 @@ import android.os.ParcelFileDescriptor; /** {@hide} */ interface IBackupTransport { -/* STOPSHIP - don't ship with this comment in place - Things the transport interface has to do: - 1. set up the connection to the destination - - set up encryption - - for Google cloud, log in using the user's gaia credential or whatever - - for adb, just set up the all-in-one destination file - 2. send each app's backup transaction - - parse the data file for key/value pointers etc - - send key/blobsize set to the Google cloud, get back quota ok/rejected response - - sd/adb doesn't preflight; no per-app quota - - app's entire change is essentially atomic - - cloud transaction encrypts then sends each key/value pair separately; we already - parsed the data when preflighting so we don't have to again here - - sd target streams raw data into encryption envelope then to sd? - 3. shut down connection to destination - - cloud: tear down connection etc - - adb: close the file -*/ /** * Ask the transport where, on local device storage, to keep backup state blobs. * This is per-transport so that mock transports used for testing can coexist with @@ -68,6 +50,17 @@ interface IBackupTransport { long requestBackupTime(); /** + * Initialize the server side storage for this device, erasing all stored data. + * The transport may send the request immediately, or may buffer it. After + * this is called, {@link #finishBackup} must be called to ensure the request + * is sent and received successfully. + * + * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far) or + * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure). + */ + int initializeDevice(); + + /** * Send one application's data to the backup destination. The transport may send * the data immediately, or may buffer it. After this is called, {@link #finishBackup} * must be called to ensure the data is sent and recorded successfully. @@ -77,10 +70,16 @@ interface IBackupTransport { * @param data The data stream that resulted from invoking the application's * BackupService.doBackup() method. This may be a pipe rather than a file on * persistent media, so it may not be seekable. - * @return false if errors occurred (the backup should be aborted and rescheduled), - * true if everything is OK so far (but {@link #finishBackup} must be called). + * @param wipeAllFirst When true, <i>all</i> backed-up data for the current device/account + * will be erased prior to the storage of the data provided here. The purpose of this + * is to provide a guarantee that no stale data exists in the restore set when the + * device begins providing backups. + * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far), + * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or + * {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has + * become lost due to inactive expiry or some other reason and needs re-initializing) */ - boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd); + int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd); /** * Erase the give application's data from the backup destination. This clears @@ -88,10 +87,9 @@ interface IBackupTransport { * the app had never yet been backed up. After this is called, {@link finishBackup} * must be called to ensure that the operation is recorded successfully. * - * @return false if errors occurred (the backup should be aborted and rescheduled), - * true if everything is OK so far (but {@link #finishBackup} must be called). + * @return the same error codes as {@link #performBackup}. */ - boolean clearBackupData(in PackageInfo packageInfo); + int clearBackupData(in PackageInfo packageInfo); /** * Finish sending application data to the backup destination. This must be @@ -99,10 +97,9 @@ interface IBackupTransport { * all data is sent. Only when this method returns true can a backup be assumed * to have succeeded. * - * @return false if errors occurred (the backup should be aborted and rescheduled), - * true if everything is OK. + * @return the same error codes as {@link #performBackup}. */ - boolean finishBackup(); + int finishBackup(); /** * Get the set of backups currently available over this transport. @@ -120,10 +117,11 @@ interface IBackupTransport { * @param token A backup token as returned by {@link #getAvailableRestoreSets}. * @param packages List of applications to restore (if data is available). * Application data will be restored in the order given. - * @return false if errors occurred (the restore should be aborted and rescheduled), - * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}). + * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call + * {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR} + * (an error occurred, the restore should be aborted and rescheduled). */ - boolean startRestore(long token, in PackageInfo[] packages); + int startRestore(long token, in PackageInfo[] packages); /** * Get the package name of the next application with data in the backup store. @@ -136,10 +134,9 @@ interface IBackupTransport { /** * Get the data for the application returned by {@link #nextRestorePackage}. * @param data An open, writable file into which the backup data should be stored. - * @return false if errors occurred (the restore should be aborted and rescheduled), - * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}). + * @return the same error codes as {@link #nextRestorePackage}. */ - boolean getRestoreData(in ParcelFileDescriptor outFd); + int getRestoreData(in ParcelFileDescriptor outFd); /** * End a restore session (aborting any in-process data transfer as necessary), diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 2facce2..4fc3edc 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -47,17 +47,22 @@ public class LocalTransport extends IBackupTransport.Stub { } - public String transportDirName() throws RemoteException { + public String transportDirName() { return TRANSPORT_DIR_NAME; } - public long requestBackupTime() throws RemoteException { + public long requestBackupTime() { // any time is a good time for local backup return 0; } - public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) - throws RemoteException { + public int initializeDevice() { + if (DEBUG) Log.v(TAG, "wiping all data"); + deleteContents(mDataDir); + return BackupConstants.TRANSPORT_OK; + } + + public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) { if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName); File packageDir = new File(mDataDir, packageInfo.packageName); @@ -95,7 +100,7 @@ public class LocalTransport extends IBackupTransport.Stub { entity.write(buf, 0, dataSize); } catch (IOException e) { Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); - return false; + return BackupConstants.TRANSPORT_ERROR; } finally { entity.close(); } @@ -103,15 +108,30 @@ public class LocalTransport extends IBackupTransport.Stub { entityFile.delete(); } } - return true; + return BackupConstants.TRANSPORT_OK; } catch (IOException e) { // oops, something went wrong. abort the operation and return error. Log.v(TAG, "Exception reading backup input:", e); - return false; + return BackupConstants.TRANSPORT_ERROR; + } + } + + // Deletes the contents but not the given directory + private void deleteContents(File dirname) { + File[] contents = dirname.listFiles(); + if (contents != null) { + for (File f : contents) { + if (f.isDirectory()) { + // delete the directory's contents then fall through + // and delete the directory itself. + deleteContents(f); + } + f.delete(); + } } } - public boolean clearBackupData(PackageInfo packageInfo) { + public int clearBackupData(PackageInfo packageInfo) { if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName); File packageDir = new File(mDataDir, packageInfo.packageName); @@ -119,12 +139,12 @@ public class LocalTransport extends IBackupTransport.Stub { f.delete(); } packageDir.delete(); - return true; + return BackupConstants.TRANSPORT_OK; } - public boolean finishBackup() throws RemoteException { + public int finishBackup() { if (DEBUG) Log.v(TAG, "finishBackup()"); - return true; + return BackupConstants.TRANSPORT_OK; } // Restore handling @@ -135,11 +155,11 @@ public class LocalTransport extends IBackupTransport.Stub { return array; } - public boolean startRestore(long token, PackageInfo[] packages) { + public int startRestore(long token, PackageInfo[] packages) { if (DEBUG) Log.v(TAG, "start restore " + token); mRestorePackages = packages; mRestorePackage = -1; - return true; + return BackupConstants.TRANSPORT_OK; } public String nextRestorePackage() { @@ -156,7 +176,7 @@ public class LocalTransport extends IBackupTransport.Stub { return ""; } - public boolean getRestoreData(ParcelFileDescriptor outFd) { + public int getRestoreData(ParcelFileDescriptor outFd) { if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName); @@ -164,9 +184,9 @@ public class LocalTransport extends IBackupTransport.Stub { // The restore set is the concatenation of the individual record blobs, // each of which is a file in the package's directory File[] blobs = packageDir.listFiles(); - if (blobs == null) { + if (blobs == null) { // nextRestorePackage() ensures the dir exists, so this is an error Log.e(TAG, "Error listing directory: " + packageDir); - return false; // nextRestorePackage() ensures the dir exists, so this is an error + return BackupConstants.TRANSPORT_ERROR; } // We expect at least some data if the directory exists in the first place @@ -187,10 +207,10 @@ public class LocalTransport extends IBackupTransport.Stub { in.close(); } } - return true; + return BackupConstants.TRANSPORT_OK; } catch (IOException e) { Log.e(TAG, "Unable to read backup records", e); - return false; + return BackupConstants.TRANSPORT_ERROR; } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 2da72df..35c66ba 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -56,7 +56,9 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 39; + private static final int VERSION = 40; + + private static int sNumSpeedSteps; private final File mFile; private final File mBackupFile; @@ -213,7 +215,7 @@ public final class BatteryStatsImpl extends BatteryStats { /** * State for keeping track of counting information. */ - public static final class Counter extends BatteryStats.Counter implements Unpluggable { + public static class Counter extends BatteryStats.Counter implements Unpluggable { int mCount; int mLoadedCount; int mLastCount; @@ -302,7 +304,22 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggedCount = mPluggedCount = mCount; } } - + + public static class SamplingCounter extends Counter { + + SamplingCounter(ArrayList<Unpluggable> unpluggables, Parcel in) { + super(unpluggables, in); + } + + SamplingCounter(ArrayList<Unpluggable> unpluggables) { + super(unpluggables); + } + + public void addCountLocked(long count) { + mCount += count; + } + } + /** * State for keeping track of timing information. */ @@ -1940,10 +1957,16 @@ public final class BatteryStatsImpl extends BatteryStats { */ long mUnpluggedForegroundTime; + SamplingCounter[] mSpeedBins; + Proc() { mUnpluggables.add(this); + mSpeedBins = new SamplingCounter[getCpuSpeedSteps()]; + for (int i = 0; i < mSpeedBins.length; i++) { + mSpeedBins[i] = new SamplingCounter(mUnpluggables); + } } - + public void unplug(long batteryUptime, long batteryRealtime) { mUnpluggedUserTime = mUserTime; mUnpluggedSystemTime = mSystemTime; @@ -1974,6 +1997,11 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mUnpluggedSystemTime); out.writeLong(mUnpluggedForegroundTime); out.writeInt(mUnpluggedStarts); + + out.writeInt(mSpeedBins.length); + for (int i = 0; i < mSpeedBins.length; i++) { + mSpeedBins[i].writeToParcel(out); + } } void readFromParcelLocked(Parcel in) { @@ -1993,6 +2021,12 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggedSystemTime = in.readLong(); mUnpluggedForegroundTime = in.readLong(); mUnpluggedStarts = in.readInt(); + + int bins = in.readInt(); + mSpeedBins = new SamplingCounter[bins]; + for (int i = 0; i < bins; i++) { + mSpeedBins[i] = new SamplingCounter(mUnpluggables, in); + } } public BatteryStatsImpl getBatteryStats() { @@ -2075,6 +2109,22 @@ public final class BatteryStatsImpl extends BatteryStats { } return val; } + + /* Called by ActivityManagerService when CPU times are updated. */ + public void addSpeedStepTimes(long[] values) { + for (int i = 0; i < mSpeedBins.length && i < values.length; i++) { + mSpeedBins[i].addCountLocked(values[i]); + } + } + + @Override + public long getTimeAtCpuSpeedStep(int speedStep, int which) { + if (speedStep < mSpeedBins.length) { + return mSpeedBins[speedStep].getCountLocked(which); + } else { + return 0; + } + } } /** @@ -2625,6 +2675,10 @@ public final class BatteryStatsImpl extends BatteryStats { readFromParcel(p); } + public void setNumSpeedSteps(int steps) { + if (sNumSpeedSteps == 0) sNumSpeedSteps = steps; + } + @Override public int getStartCount() { return mStartCount; @@ -2853,6 +2907,11 @@ public final class BatteryStatsImpl extends BatteryStats { return mDischargeCurrentLevel; } + @Override + public int getCpuSpeedSteps() { + return sNumSpeedSteps; + } + /** * Retrieve the statistics object for a particular uid, creating if needed. */ @@ -3063,7 +3122,9 @@ public final class BatteryStatsImpl extends BatteryStats { getKernelWakelockTimerLocked(kwltName).readSummaryFromParcelLocked(in); } } - + + sNumSpeedSteps = in.readInt(); + final int NU = in.readInt(); for (int iu = 0; iu < NU; iu++) { int uid = in.readInt(); @@ -3206,6 +3267,7 @@ public final class BatteryStatsImpl extends BatteryStats { } } + out.writeInt(sNumSpeedSteps); final int NU = mUidStats.size(); out.writeInt(NU); for (int iu = 0; iu < NU; iu++) { @@ -3404,6 +3466,8 @@ public final class BatteryStatsImpl extends BatteryStats { mFullTimers.clear(); mWindowTimers.clear(); + sNumSpeedSteps = in.readInt(); + int numUids = in.readInt(); mUidStats.clear(); for (int i = 0; i < numUids; i++) { @@ -3484,7 +3548,9 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } } - + + out.writeInt(sNumSpeedSteps); + int size = mUidStats.size(); out.writeInt(size); for (int i = 0; i < size; i++) { diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 94f703a..4b4b717 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -47,14 +47,9 @@ public class PowerProfile { public static final String POWER_CPU_IDLE = "cpu.idle"; /** - * Power consumption when CPU is running at normal speed. - */ - public static final String POWER_CPU_NORMAL = "cpu.normal"; - - /** - * Power consumption when CPU is running at full speed. + * Power consumption when CPU is in power collapse mode. */ - public static final String POWER_CPU_FULL = "cpu.full"; + public static final String POWER_CPU_ACTIVE = "cpu.active"; /** * Power consumption when WiFi driver is scanning for networks. @@ -124,6 +119,8 @@ public class PowerProfile { */ public static final String POWER_VIDEO = "dsp.video"; + public static final String POWER_CPU_SPEEDS = "cpu.speeds"; + static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>(); private static final String TAG_DEVICE = "device"; @@ -214,10 +211,10 @@ public class PowerProfile { } /** - * Returns the average current in mA consumed by the subsystem for the given level. + * Returns the average current in mA consumed by the subsystem for the given level. * @param type the subsystem type * @param level the level of power at which the subsystem is running. For instance, the - * signal strength of the cell network between 0 and 4 (if there are 4 bars max.). + * signal strength of the cell network between 0 and 4 (if there are 4 bars max.) * If there is no data for multiple levels, the level is ignored. * @return the average current in milliAmps. */ @@ -240,4 +237,12 @@ public class PowerProfile { return 0; } } + + public int getNumSpeedSteps() { + Object value = sPowerMap.get(POWER_CPU_SPEEDS); + if (value != null && value instanceof Double[]) { + return ((Double[])value).length; + } + return 1; // Only one speed + } } diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java index 4ec597c..35d637d 100644 --- a/core/java/com/android/internal/widget/ContactHeaderWidget.java +++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java @@ -63,6 +63,7 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList private static final String TAG = "ContactHeaderWidget"; private TextView mDisplayNameView; + private View mAggregateBadge; private TextView mPhoneticNameView; private CheckBox mStarredView; private FasttrackBadgeWidget mPhotoView; @@ -159,6 +160,8 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList mDisplayNameView = (TextView) findViewById(R.id.name); mDisplayNameView.setOnLongClickListener(this); + mAggregateBadge = findViewById(R.id.aggregate_badge); + mAggregateBadge.setVisibility(View.GONE); mPhoneticNameView = (TextView) findViewById(R.id.phonetic_name); @@ -248,7 +251,9 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX); bindFromContactUri(Contacts.getLookupUri(contactId, lookupKey)); } else { - setDisplayName((String) cookie, null); + String phoneNumber = (String) cookie; + setDisplayName(phoneNumber, null); + mPhotoView.assignContactFromPhone(phoneNumber, true); } break; } @@ -259,7 +264,9 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX); bindFromContactUri(Contacts.getLookupUri(contactId, lookupKey)); } else { - setDisplayName((String) cookie, null); + String emailAddress = (String) cookie; + setDisplayName(emailAddress, null); + mPhotoView.assignContactFromEmail(emailAddress, true); } break; } @@ -280,6 +287,13 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList } /** + * Turn on/off showing of the aggregate bage element. + */ + public void showAggregateBadge(boolean showBagde) { + mAggregateBadge.setVisibility(showBagde ? View.VISIBLE : View.GONE); + } + + /** * Turn on/off showing of the star element. */ public void showStar(boolean showStar) { @@ -306,6 +320,7 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList */ public void setContactUri(Uri uri) { mContactUri = uri; + mPhotoView.assignContactUri(uri); } /** @@ -398,7 +413,7 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList */ public void bindFromPhoneNumber(String number) { mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, number, - Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number), + Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)), PHONE_LOOKUP_PROJECTION, null, null, null); } diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java index 712f1bf..426cef5 100644 --- a/core/java/com/android/internal/widget/RotarySelector.java +++ b/core/java/com/android/internal/widget/RotarySelector.java @@ -25,7 +25,9 @@ import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; -import android.view.animation.AccelerateInterpolator; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.animation.DecelerateInterpolator; import static android.view.animation.AnimationUtils.currentAnimationTimeMillis; import com.android.internal.R; @@ -59,21 +61,18 @@ public class RotarySelector extends View { private int mLeftHandleX; private int mRightHandleX; - // current offset of user's dragging - private int mTouchDragOffset = 0; + // current offset of rotary widget along the x axis + private int mRotaryOffsetX = 0; // state of the animation used to bring the handle back to its start position when // the user lets go before triggering an action private boolean mAnimating = false; - private long mAnimationEndTime; - private int mAnimatingDelta; - private AccelerateInterpolator mInterpolator; + private long mAnimationStartTime; + private long mAnimationDuration; + private int mAnimatingDeltaXStart; // the animation will interpolate from this delta to zero + private int mAnimatingDeltaXEnd; - /** - * True after triggering an action if the user of {@link OnDialTriggerListener} wants to - * freeze the UI (until they transition to another screen). - */ - private boolean mFrozen = false; + private DecelerateInterpolator mInterpolator; /** * If the user is currently dragging something. @@ -91,8 +90,8 @@ public class RotarySelector extends View { // Vibration (haptic feedback) private Vibrator mVibrator; - private static final long VIBRATE_SHORT = 60; // msec - private static final long VIBRATE_LONG = 100; // msec + private static final long VIBRATE_SHORT = 30; // msec + private static final long VIBRATE_LONG = 60; // msec /** * The drawable for the arrows need to be scrunched this many dips towards the rotary bg below @@ -108,17 +107,37 @@ public class RotarySelector extends View { /** * How far from the edge of the screen the user must drag to trigger the event. */ - private static final int EDGE_TRIGGER_DIP = 65; + private static final int EDGE_TRIGGER_DIP = 100; /** * Dimensions of arc in background drawable. */ static final int OUTER_ROTARY_RADIUS_DIP = 390; static final int ROTARY_STROKE_WIDTH_DIP = 83; - private static final int ANIMATION_DURATION_MILLIS = 300; + static final int SNAP_BACK_ANIMATION_DURATION_MILLIS = 300; + static final int SPIN_ANIMATION_DURATION_MILLIS = 800; - private static final boolean DRAW_CENTER_DIMPLE = false; + private static final boolean DRAW_CENTER_DIMPLE = true; private int mEdgeTriggerThresh; + private int mDimpleWidth; + private int mBackgroundWidth; + private int mBackgroundHeight; + private final int mOuterRadius; + private final int mInnerRadius; + private int mDimpleSpacing; + + private VelocityTracker mVelocityTracker; + private int mMinimumVelocity; + private int mMaximumVelocity; + + /** + * The number of dimples we are flinging when we do the "spin" animation. Used to know when to + * wrap the icons back around so they "rotate back" onto the screen. + * @see #updateAnimation() + */ + private int mDimplesOfFling = 0; + + public RotarySelector(Context context) { this(context, null); @@ -152,9 +171,31 @@ public class RotarySelector extends View { mArrowLongLeft.setBounds(0, 0, arrowW, arrowH); mArrowLongRight.setBounds(0, 0, arrowW, arrowH); - mInterpolator = new AccelerateInterpolator(); + mInterpolator = new DecelerateInterpolator(1f); mEdgeTriggerThresh = (int) (mDensity * EDGE_TRIGGER_DIP); + + mDimpleWidth = mDimple.getIntrinsicWidth(); + + mBackgroundWidth = mBackground.getIntrinsicWidth(); + mBackgroundHeight = mBackground.getIntrinsicHeight(); + mOuterRadius = (int) (mDensity * OUTER_ROTARY_RADIUS_DIP); + mInnerRadius = (int) ((OUTER_ROTARY_RADIUS_DIP - ROTARY_STROKE_WIDTH_DIP) * mDensity); + + final ViewConfiguration configuration = ViewConfiguration.get(mContext); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity() * 2; + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + mLeftHandleX = (int) (EDGE_PADDING_DIP * mDensity) + mDimpleWidth / 2; + mRightHandleX = + getWidth() - (int) (EDGE_PADDING_DIP * mDensity) - mDimpleWidth / 2; + + mDimpleSpacing = (getWidth() / 2) - mLeftHandleX; } /** @@ -214,7 +255,7 @@ public class RotarySelector extends View { final int width = MeasureSpec.getSize(widthMeasureSpec); // screen width final int arrowH = mArrowShortLeftAndRight.getIntrinsicHeight(); - final int backgroundH = mBackground.getIntrinsicHeight(); + final int backgroundH = mBackgroundHeight; // by making the height less than arrow + bg, arrow and bg will be scrunched together, // overlaying somewhat (though on transparent portions of the drawable). @@ -224,44 +265,26 @@ public class RotarySelector extends View { setMeasuredDimension(width, backgroundH + arrowH - arrowScrunch); } - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - - mLeftHandleX = (int) (EDGE_PADDING_DIP * mDensity) + mDimple.getIntrinsicWidth() / 2; - mRightHandleX = - getWidth() - (int) (EDGE_PADDING_DIP * mDensity) - mDimple.getIntrinsicWidth() / 2; - } - // private Paint mPaint = new Paint(); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (DBG) { - log(String.format("onDraw: mAnimating=%s, mTouchDragOffset=%d, mGrabbedState=%d," + - "mFrozen=%s", - mAnimating, mTouchDragOffset, mGrabbedState, mFrozen)); + log(String.format("onDraw: mAnimating=%s, mRotaryOffsetX=%d, mGrabbedState=%d", + mAnimating, mRotaryOffsetX, mGrabbedState)); } final int height = getHeight(); // update animating state before we draw anything - if (mAnimating && !mFrozen) { - long millisLeft = mAnimationEndTime - currentAnimationTimeMillis(); - if (DBG) log("millisleft for animating: " + millisLeft); - if (millisLeft <= 0) { - reset(); - } else { - float interpolation = mInterpolator.getInterpolation( - (float) millisLeft / ANIMATION_DURATION_MILLIS); - mTouchDragOffset = (int) (mAnimatingDelta * interpolation); - } + if (mAnimating) { + updateAnimation(); } // Background: - final int backgroundW = mBackground.getIntrinsicWidth(); - final int backgroundH = mBackground.getIntrinsicHeight(); + final int backgroundW = mBackgroundWidth; + final int backgroundH = mBackgroundHeight; final int backgroundY = height - backgroundH; if (DBG) log("- Background INTRINSIC: " + backgroundW + " x " + backgroundH); mBackground.setBounds(0, backgroundY, @@ -293,16 +316,13 @@ public class RotarySelector extends View { // float or = OUTER_ROTARY_RADIUS_DIP * mDensity; // canvas.drawCircle(getWidth() / 2, or + mBackground.getBounds().top, or, mPaint); - final int outerRadius = (int) (mDensity * OUTER_ROTARY_RADIUS_DIP); - final int innerRadius = - (int) ((OUTER_ROTARY_RADIUS_DIP - ROTARY_STROKE_WIDTH_DIP) * mDensity); final int bgTop = mBackground.getBounds().top; { - final int xOffset = mLeftHandleX + mTouchDragOffset; + final int xOffset = mLeftHandleX + mRotaryOffsetX; final int drawableY = getYOnArc( mBackground, - innerRadius, - outerRadius, + mInnerRadius, + mOuterRadius, xOffset); drawCentered(mDimple, canvas, xOffset, drawableY + bgTop); @@ -312,22 +332,22 @@ public class RotarySelector extends View { } if (DRAW_CENTER_DIMPLE) { - final int xOffset = getWidth() / 2 + mTouchDragOffset; + final int xOffset = getWidth() / 2 + mRotaryOffsetX; final int drawableY = getYOnArc( mBackground, - innerRadius, - outerRadius, + mInnerRadius, + mOuterRadius, xOffset); drawCentered(mDimple, canvas, xOffset, drawableY + bgTop); } { - final int xOffset = mRightHandleX + mTouchDragOffset; + final int xOffset = mRightHandleX + mRotaryOffsetX; final int drawableY = getYOnArc( mBackground, - innerRadius, - outerRadius, + mInnerRadius, + mOuterRadius, xOffset); drawCentered(mDimple, canvas, xOffset, drawableY + bgTop); @@ -336,7 +356,33 @@ public class RotarySelector extends View { } } - if (mAnimating) invalidate(); + // draw extra left hand dimples + int dimpleLeft = mRotaryOffsetX + mLeftHandleX - mDimpleSpacing; + final int halfdimple = mDimpleWidth / 2; + while (dimpleLeft > -halfdimple) { + final int drawableY = getYOnArc( + mBackground, + mInnerRadius, + mOuterRadius, + dimpleLeft); + + drawCentered(mDimple, canvas, dimpleLeft, drawableY + bgTop); + dimpleLeft -= mDimpleSpacing; + } + + // draw extra right hand dimples + int dimpleRight = mRotaryOffsetX + mRightHandleX + mDimpleSpacing; + final int rightThresh = mRight + halfdimple; + while (dimpleRight < rightThresh) { + final int drawableY = getYOnArc( + mBackground, + mInnerRadius, + mOuterRadius, + dimpleRight); + + drawCentered(mDimple, canvas, dimpleRight, drawableY + bgTop); + dimpleRight += mDimpleSpacing; + } } /** @@ -383,12 +429,17 @@ public class RotarySelector extends View { */ @Override public boolean onTouchEvent(MotionEvent event) { - if (mAnimating || mFrozen) { + if (mAnimating) { return true; } + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(event); + final int eventX = (int) event.getX(); - final int hitWindow = mDimple.getIntrinsicWidth(); + final int hitWindow = mDimpleWidth; final int action = event.getAction(); switch (action) { @@ -400,12 +451,12 @@ public class RotarySelector extends View { invalidate(); } if (eventX < mLeftHandleX + hitWindow) { - mTouchDragOffset = eventX - mLeftHandleX; + mRotaryOffsetX = eventX - mLeftHandleX; mGrabbedState = LEFT_HANDLE_GRABBED; invalidate(); vibrate(VIBRATE_SHORT); } else if (eventX > mRightHandleX - hitWindow) { - mTouchDragOffset = eventX - mRightHandleX; + mRotaryOffsetX = eventX - mRightHandleX; mGrabbedState = RIGHT_HANDLE_GRABBED; invalidate(); vibrate(VIBRATE_SHORT); @@ -415,18 +466,38 @@ public class RotarySelector extends View { case MotionEvent.ACTION_MOVE: if (DBG) log("touch-move"); if (mGrabbedState == LEFT_HANDLE_GRABBED) { - mTouchDragOffset = eventX - mLeftHandleX; + mRotaryOffsetX = eventX - mLeftHandleX; invalidate(); if (eventX >= getRight() - mEdgeTriggerThresh && !mTriggered) { mTriggered = true; - mFrozen = dispatchTriggerEvent(OnDialTriggerListener.LEFT_HANDLE); + dispatchTriggerEvent(OnDialTriggerListener.LEFT_HANDLE); + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + final int velocity = Math.max(mMinimumVelocity, (int) velocityTracker.getXVelocity()); + mDimplesOfFling = Math.max( + 8, + Math.abs(velocity / mDimpleSpacing)); + startAnimationWithVelocity( + eventX - mLeftHandleX, + mDimplesOfFling * mDimpleSpacing, + velocity); } } else if (mGrabbedState == RIGHT_HANDLE_GRABBED) { - mTouchDragOffset = eventX - mRightHandleX; + mRotaryOffsetX = eventX - mRightHandleX; invalidate(); if (eventX <= mEdgeTriggerThresh && !mTriggered) { mTriggered = true; - mFrozen = dispatchTriggerEvent(OnDialTriggerListener.RIGHT_HANDLE); + dispatchTriggerEvent(OnDialTriggerListener.RIGHT_HANDLE); + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + final int velocity = Math.min(-mMinimumVelocity, (int) velocityTracker.getXVelocity()); + mDimplesOfFling = Math.max( + 8, + Math.abs(velocity / mDimpleSpacing)); + startAnimationWithVelocity( + eventX - mRightHandleX, + -(mDimplesOfFling * mDimpleSpacing), + velocity); } } break; @@ -435,32 +506,85 @@ public class RotarySelector extends View { // handle animating back to start if they didn't trigger if (mGrabbedState == LEFT_HANDLE_GRABBED && Math.abs(eventX - mLeftHandleX) > 5) { - mAnimating = true; - mAnimationEndTime = currentAnimationTimeMillis() + ANIMATION_DURATION_MILLIS; - mAnimatingDelta = eventX - mLeftHandleX; + // set up "snap back" animation + startAnimation(eventX - mLeftHandleX, 0, SNAP_BACK_ANIMATION_DURATION_MILLIS); } else if (mGrabbedState == RIGHT_HANDLE_GRABBED && Math.abs(eventX - mRightHandleX) > 5) { - mAnimating = true; - mAnimationEndTime = currentAnimationTimeMillis() + ANIMATION_DURATION_MILLIS; - mAnimatingDelta = eventX - mRightHandleX; + // set up "snap back" animation + startAnimation(eventX - mRightHandleX, 0, SNAP_BACK_ANIMATION_DURATION_MILLIS); } - - mTouchDragOffset = 0; + mRotaryOffsetX = 0; mGrabbedState = NOTHING_GRABBED; invalidate(); + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); // wishin' we had generational GC + mVelocityTracker = null; + } break; case MotionEvent.ACTION_CANCEL: if (DBG) log("touch-cancel"); reset(); invalidate(); + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } break; } return true; } + private void startAnimation(int startX, int endX, int duration) { + mAnimating = true; + mAnimationStartTime = currentAnimationTimeMillis(); + mAnimationDuration = duration; + mAnimatingDeltaXStart = startX; + mAnimatingDeltaXEnd = endX; + mGrabbedState = NOTHING_GRABBED; + mDimplesOfFling = 0; + invalidate(); + } + + private void startAnimationWithVelocity(int startX, int endX, int pixelsPerSecond) { + mAnimating = true; + mAnimationStartTime = currentAnimationTimeMillis(); + mAnimationDuration = 1000 * (endX - startX) / pixelsPerSecond; + mAnimatingDeltaXStart = startX; + mAnimatingDeltaXEnd = endX; + mGrabbedState = NOTHING_GRABBED; + invalidate(); + } + + private void updateAnimation() { + final long millisSoFar = currentAnimationTimeMillis() - mAnimationStartTime; + final long millisLeft = mAnimationDuration - millisSoFar; + final int totalDeltaX = mAnimatingDeltaXStart - mAnimatingDeltaXEnd; + if (DBG) log("millisleft for animating: " + millisLeft); + if (millisLeft <= 0) { + reset(); + return; + } + // from 0 to 1 as animation progresses + float interpolation = + mInterpolator.getInterpolation((float) millisSoFar / mAnimationDuration); + final int dx = (int) (totalDeltaX * (1 - interpolation)); + mRotaryOffsetX = mAnimatingDeltaXEnd + dx; + if (mDimplesOfFling > 0) { + if (mRotaryOffsetX < 4 * mDimpleSpacing) { + // wrap around on fling left + mRotaryOffsetX += (4 + mDimplesOfFling - 4) * mDimpleSpacing; + } else if (mRotaryOffsetX > 4 * mDimpleSpacing) { + // wrap around on fling right + mRotaryOffsetX -= (4 + mDimplesOfFling - 4) * mDimpleSpacing; + } + } + invalidate(); + } + private void reset() { mAnimating = false; - mTouchDragOffset = 0; + mRotaryOffsetX = 0; + mDimplesOfFling = 0; mGrabbedState = NOTHING_GRABBED; mTriggered = false; } @@ -470,7 +594,8 @@ public class RotarySelector extends View { */ private synchronized void vibrate(long duration) { if (mVibrator == null) { - mVibrator = (android.os.Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); + mVibrator = (android.os.Vibrator) + getContext().getSystemService(Context.VIBRATOR_SERVICE); } mVibrator.vibrate(duration); } @@ -504,12 +629,11 @@ public class RotarySelector extends View { /** * Dispatches a trigger event to our listener. */ - private boolean dispatchTriggerEvent(int whichHandle) { + private void dispatchTriggerEvent(int whichHandle) { vibrate(VIBRATE_LONG); if (mOnDialTriggerListener != null) { - return mOnDialTriggerListener.onDialTrigger(this, whichHandle); + mOnDialTriggerListener.onDialTrigger(this, whichHandle); } - return false; } /** @@ -530,22 +654,13 @@ public class RotarySelector extends View { public static final int RIGHT_HANDLE = 2; /** - * @hide - * The center handle is currently unused. - */ - public static final int CENTER_HANDLE = 3; - - /** * Called when the dial is triggered. * * @param v The view that was triggered * @param whichHandle Which "dial handle" the user grabbed, - * either {@link #LEFT_HANDLE}, {@link #RIGHT_HANDLE}, or - * {@link #CENTER_HANDLE}. - * @return Whether the widget should freeze (e.g when the action goes to another screen, - * you want the UI to stay put until the transition occurs). + * either {@link #LEFT_HANDLE}, {@link #RIGHT_HANDLE}. */ - boolean onDialTrigger(View v, int whichHandle); + void onDialTrigger(View v, int whichHandle); } diff --git a/core/java/com/google/android/mms/pdu/GenericPdu.java b/core/java/com/google/android/mms/pdu/GenericPdu.java index 46c6e00..705de6a 100644 --- a/core/java/com/google/android/mms/pdu/GenericPdu.java +++ b/core/java/com/google/android/mms/pdu/GenericPdu.java @@ -89,4 +89,25 @@ public class GenericPdu { public void setMmsVersion(int value) throws InvalidHeaderValueException { mPduHeaders.setOctet(value, PduHeaders.MMS_VERSION); } + + /** + * Get From value. + * From-value = Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + * + * @return the value + */ + public EncodedStringValue getFrom() { + return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); + } + + /** + * Set From value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setFrom(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); + } } diff --git a/core/java/com/google/android/mms/pdu/PduComposer.java b/core/java/com/google/android/mms/pdu/PduComposer.java index 094e992..8b31936 100644 --- a/core/java/com/google/android/mms/pdu/PduComposer.java +++ b/core/java/com/google/android/mms/pdu/PduComposer.java @@ -450,6 +450,29 @@ public class PduComposer { appendQuotedString(str.getBytes()); } + private EncodedStringValue appendAddressType(EncodedStringValue address) { + EncodedStringValue temp = null; + + try { + int addressType = checkAddressType(address.getString()); + temp = EncodedStringValue.copy(address); + if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) { + // Phone number. + temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes()); + } else if (PDU_IPV4_ADDRESS_TYPE == addressType) { + // Ipv4 address. + temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes()); + } else if (PDU_IPV6_ADDRESS_TYPE == addressType) { + // Ipv6 address. + temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes()); + } + } catch (NullPointerException e) { + return null; + } + + return temp; + } + /** * Append header to mMessage. */ @@ -489,21 +512,8 @@ public class PduComposer { EncodedStringValue temp; for (int i = 0; i < addr.length; i++) { - try { - int addressType = checkAddressType(addr[i].getString()); - temp = EncodedStringValue.copy(addr[i]); - if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) { - // Phone number. - temp.appendTextString( - STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes()); - } else if (PDU_IPV4_ADDRESS_TYPE == addressType) { - // Ipv4 address. - temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes()); - } else if (PDU_IPV6_ADDRESS_TYPE == addressType) { - // Ipv6 address. - temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes()); - } - } catch (NullPointerException e) { + temp = appendAddressType(addr[i]); + if (temp == null) { return PDU_COMPOSE_CONTENT_ERROR; } @@ -530,7 +540,13 @@ public class PduComposer { // Address-present-token = <Octet 128> append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN); - appendEncodedString(from); + + temp = appendAddressType(from); + if (temp == null) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + appendEncodedString(temp); int flen = fstart.getLength(); mStack.pop(); diff --git a/core/java/com/google/android/mms/pdu/ReadRecInd.java b/core/java/com/google/android/mms/pdu/ReadRecInd.java index 0a4dbf0..880e3ac 100644 --- a/core/java/com/google/android/mms/pdu/ReadRecInd.java +++ b/core/java/com/google/android/mms/pdu/ReadRecInd.java @@ -73,27 +73,6 @@ public class ReadRecInd extends GenericPdu { } /** - * Get From value. - * From-value = Value-length - * (Address-present-token Encoded-string-value | Insert-address-token) - * - * @return the value - */ - public EncodedStringValue getFrom() { - return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); - } - - /** - * Set From value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setFrom(EncodedStringValue value) { - mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); - } - - /** * Get Message-ID value. * * @return the value diff --git a/core/java/com/google/android/mms/pdu/SendReq.java b/core/java/com/google/android/mms/pdu/SendReq.java index 9ea6e47..597cd00 100644 --- a/core/java/com/google/android/mms/pdu/SendReq.java +++ b/core/java/com/google/android/mms/pdu/SendReq.java @@ -226,27 +226,6 @@ public class SendReq extends MultimediaMessagePdu { } /** - * Get From value. - * From-value = Value-length - * (Address-present-token Encoded-string-value | Insert-address-token) - * - * @return the value - */ - public EncodedStringValue getFrom() { - return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); - } - - /** - * Set From value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setFrom(EncodedStringValue value) { - mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); - } - - /** * Get X-Mms-Message-Class value. * Message-class-value = Class-identifier | Token-text * Class-identifier = Personal | Advertisement | Informational | Auto diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index ce2b10c..6b92994 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -55,7 +55,7 @@ private: jobject mCameraJObjectWeak; // weak reference to java object jclass mCameraJClass; // strong reference to java class - sp<Camera> mCamera; // strong reference to native object + sp<Camera> mCamera; // strong reference to native object Mutex mLock; }; @@ -391,20 +391,26 @@ static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz) } } -static jint android_hardware_Camera_lock(JNIEnv *env, jobject thiz) +static void android_hardware_Camera_lock(JNIEnv *env, jobject thiz) { LOGV("lock"); sp<Camera> camera = get_native_camera(env, thiz, NULL); - if (camera == 0) return INVALID_OPERATION; - return (jint) camera->lock(); + if (camera == 0) return; + + if (camera->lock() != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "lock failed"); + } } -static jint android_hardware_Camera_unlock(JNIEnv *env, jobject thiz) +static void android_hardware_Camera_unlock(JNIEnv *env, jobject thiz) { LOGV("unlock"); sp<Camera> camera = get_native_camera(env, thiz, NULL); - if (camera == 0) return INVALID_OPERATION; - return (jint) camera->unlock(); + if (camera == 0) return; + + if (camera->unlock() != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "unlock failed"); + } } //------------------------------------------------- @@ -450,10 +456,10 @@ static JNINativeMethod camMethods[] = { "()V", (void*)android_hardware_Camera_reconnect }, { "lock", - "()I", + "()V", (void*)android_hardware_Camera_lock }, { "unlock", - "()I", + "()V", (void*)android_hardware_Camera_unlock }, }; diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index e703ed8..e37e832 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -48,6 +48,8 @@ static jmethodID method_onDeviceRemoved; static jmethodID method_onDeviceDisconnectRequested; static jmethodID method_onCreatePairedDeviceResult; +static jmethodID method_onCreateDeviceResult; +static jmethodID method_onDiscoverServicesResult; static jmethodID method_onGetDeviceServiceChannelResult; static jmethodID method_onRequestPinCode; @@ -92,6 +94,10 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult", "(Ljava/lang/String;I)V"); + method_onCreateDeviceResult = env->GetMethodID(clazz, "onCreateDeviceResult", + "(Ljava/lang/String;Z)V"); + method_onDiscoverServicesResult = env->GetMethodID(clazz, "onDiscoverServicesResult", + "(Ljava/lang/String;Z)V"); method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;)Z"); @@ -1097,6 +1103,54 @@ done: free(user); } +void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) { + LOGV(__FUNCTION__); + + native_data_t *nat = (native_data_t *)n; + const char *address= (const char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env; + nat->vm->GetEnv((void**)&env, nat->envVer); + + LOGV("... Address = %s", address); + + bool result = JNI_TRUE; + if (dbus_set_error_from_message(&err, msg)) { + LOG_AND_FREE_DBUS_ERROR(&err); + result = JNI_FALSE; + } + env->CallVoidMethod(nat->me, + method_onCreateDeviceResult, + env->NewStringUTF(address), + result); + free(user); +} + +void onDiscoverServicesResult(DBusMessage *msg, void *user, void *n) { + LOGV(__FUNCTION__); + + native_data_t *nat = (native_data_t *)n; + const char *path = (const char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env; + nat->vm->GetEnv((void**)&env, nat->envVer); + + LOGV("... Device Path = %s", path); + + bool result = JNI_TRUE; + if (dbus_set_error_from_message(&err, msg)) { + LOG_AND_FREE_DBUS_ERROR(&err); + result = JNI_FALSE; + } + env->CallVoidMethod(nat->me, + method_onDiscoverServicesResult, + env->NewStringUTF(path), + result); + free(user); +} + void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) { LOGV(__FUNCTION__); diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index 0b71acb..c2f93eea 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -66,6 +66,8 @@ extern DBusHandlerResult agent_event_filter(DBusConnection *conn, DBusMessage *msg, void *data); void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat); +void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat); +void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat); /** Get native data stored in the opaque (Java code maintained) pointer mNativeData @@ -385,33 +387,24 @@ static jboolean cancelDeviceCreationNative(JNIEnv *env, jobject object, static jboolean removeDeviceNative(JNIEnv *env, jobject object, jstring object_path) { LOGV(__FUNCTION__); - jboolean result = JNI_FALSE; #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); if (nat) { const char *c_object_path = env->GetStringUTFChars(object_path, NULL); - DBusError err; - dbus_error_init(&err); - DBusMessage *reply = - dbus_func_args_error(env, nat->conn, &err, - get_adapter_path(env, object), - DBUS_ADAPTER_IFACE, "RemoveDevice", - DBUS_TYPE_OBJECT_PATH, &c_object_path, - DBUS_TYPE_INVALID); - if (!reply) { - if (dbus_error_is_set(&err)) { - LOG_AND_FREE_DBUS_ERROR(&err); - } else - LOGE("DBus reply is NULL in function %s", __FUNCTION__); - result = JNI_FALSE; - } else { - result = JNI_TRUE; - } + bool ret = dbus_func_args_async(env, nat->conn, -1, + NULL, + NULL, + NULL, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, + "RemoveDevice", + DBUS_TYPE_OBJECT_PATH, &c_object_path, + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(object_path, c_object_path); - if (reply) dbus_message_unref(reply); + return ret ? JNI_TRUE : JNI_FALSE; } #endif - return result; + return JNI_FALSE; } static jint enableNative(JNIEnv *env, jobject object) { @@ -757,6 +750,75 @@ static jboolean setDevicePropertyBooleanNative(JNIEnv *env, jobject object, #endif } + +static jboolean createDeviceNative(JNIEnv *env, jobject object, + jstring address) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); + strlcpy(context_address, c_address, BTADDR_SIZE); // for callback + + bool ret = dbus_func_args_async(env, nat->conn, -1, + onCreateDeviceResult, + context_address, + eventLoopNat, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, + "CreateDevice", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean discoverServicesNative(JNIEnv *env, jobject object, + jstring path, jstring pattern) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + const char *c_pattern = env->GetStringUTFChars(pattern, NULL); + int len = env->GetStringLength(path) + 1; + char *context_path = (char *)calloc(len, sizeof(char)); + strlcpy(context_path, c_path, len); // for callback + + LOGV("... Object Path = %s", c_path); + LOGV("... Pattern = %s, strlen = %d", c_pattern, strlen(c_pattern)); + + bool ret = dbus_func_args_async(env, nat->conn, -1, + onDiscoverServicesResult, + context_path, + eventLoopNat, + c_path, + DBUS_DEVICE_IFACE, + "DiscoverServices", + DBUS_TYPE_STRING, &c_pattern, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); + env->ReleaseStringUTFChars(pattern, c_pattern); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"classInitNative", "()V", (void*)classInitNative}, @@ -797,6 +859,8 @@ static JNINativeMethod sMethods[] = { (void *)cancelPairingUserInputNative}, {"setDevicePropertyBooleanNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setDevicePropertyBooleanNative}, + {"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative}, + {"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative}, }; int register_android_server_BluetoothService(JNIEnv *env) { diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml index 9d1ef53..f1258e8 100644 --- a/core/res/res/anim/activity_close_enter.xml +++ b/core/res/res/anim/activity_close_enter.xml @@ -21,5 +21,5 @@ android:interpolator="@anim/decelerate_interpolator" android:zAdjustment="top"> <translate android:fromXDelta="-100%" android:toXDelta="0" - android:duration="@android:integer/config_mediumAnimTime"/> + android:duration="@android:integer/config_shortAnimTime"/> </set> diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml index 47cb6d6..bf3d8cd3 100644 --- a/core/res/res/anim/activity_close_exit.xml +++ b/core/res/res/anim/activity_close_exit.xml @@ -20,5 +20,5 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator"> <translate android:fromXDelta="0%" android:toXDelta="33%" - android:duration="@android:integer/config_mediumAnimTime"/> + android:duration="@android:integer/config_shortAnimTime"/> </set> diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml index e4c7e9b..a9ea381 100644 --- a/core/res/res/anim/activity_open_enter.xml +++ b/core/res/res/anim/activity_open_enter.xml @@ -20,5 +20,5 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator"> <translate android:fromXDelta="33%" android:toXDelta="0" - android:duration="@android:integer/config_mediumAnimTime"/> + android:duration="@android:integer/config_shortAnimTime"/> </set> diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml index 9d47b7f..b04b79e 100644 --- a/core/res/res/anim/activity_open_exit.xml +++ b/core/res/res/anim/activity_open_exit.xml @@ -21,5 +21,5 @@ android:interpolator="@anim/decelerate_interpolator" android:zAdjustment="top"> <translate android:fromXDelta="0%" android:toXDelta="-100%" - android:duration="@android:integer/config_mediumAnimTime"/> + android:duration="@android:integer/config_shortAnimTime"/> </set> diff --git a/core/res/res/anim/dialog_enter.xml b/core/res/res/anim/dialog_enter.xml index 167f4bc..d4983c6 100644 --- a/core/res/res/anim/dialog_enter.xml +++ b/core/res/res/anim/dialog_enter.xml @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* //device/apps/common/res/anim/fade_in.xml -** +/* ** Copyright 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +21,7 @@ android:interpolator="@anim/decelerate_interpolator"> <scale android:fromXScale="0.9" android:toXScale="1.0" android:fromYScale="0.9" android:toYScale="1.0" - android:pivotX="50%p" android:pivotY="50%p" + android:pivotX="50%" android:pivotY="50%" android:duration="@android:integer/config_shortAnimTime" /> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="@android:integer/config_shortAnimTime" /> diff --git a/core/res/res/anim/dialog_exit.xml b/core/res/res/anim/dialog_exit.xml index d412cfb..2aa629a 100644 --- a/core/res/res/anim/dialog_exit.xml +++ b/core/res/res/anim/dialog_exit.xml @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* //device/apps/common/res/anim/fade_out.xml -** +/* ** Copyright 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,11 +16,12 @@ ** limitations under the License. */ --> + <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_interpolator"> <scale android:fromXScale="1.0" android:toXScale="0.9" android:fromYScale="1.0" android:toYScale="0.9" - android:pivotX="50%p" android:pivotY="50%p" + android:pivotX="50%" android:pivotY="50%" android:duration="@android:integer/config_shortAnimTime" /> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="@android:integer/config_shortAnimTime"/> diff --git a/core/res/res/anim/recent_enter.xml b/core/res/res/anim/recent_enter.xml index deeb96b..8faa2c1 100644 --- a/core/res/res/anim/recent_enter.xml +++ b/core/res/res/anim/recent_enter.xml @@ -19,10 +19,10 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator"> - <scale android:fromXScale="2.0" android:toXScale="1.0" - android:fromYScale="2.0" android:toYScale="1.0" - android:pivotX="50%p" android:pivotY="50%p" - android:duration="@android:integer/config_mediumAnimTime" /> + <scale android:fromXScale="0.9" android:toXScale="1.0" + android:fromYScale="0.9" android:toYScale="1.0" + android:pivotX="50%" android:pivotY="50%" + android:duration="@android:integer/config_shortAnimTime" /> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" - android:duration="@android:integer/config_mediumAnimTime"/> + android:duration="@android:integer/config_shortAnimTime" /> </set> diff --git a/core/res/res/anim/recent_exit.xml b/core/res/res/anim/recent_exit.xml index fed7014..9399329 100644 --- a/core/res/res/anim/recent_exit.xml +++ b/core/res/res/anim/recent_exit.xml @@ -18,12 +18,11 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:interpolator="@anim/decelerate_interpolator" - android:zAdjustment="top"> - <scale android:fromXScale="1.0" android:toXScale="2.0" - android:fromYScale="1.0" android:toYScale="2.0" - android:pivotX="50%p" android:pivotY="50%p" - android:duration="@android:integer/config_mediumAnimTime" /> - <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:duration="@android:integer/config_mediumAnimTime"/> + android:interpolator="@anim/accelerate_interpolator"> + <scale android:fromXScale="1.0" android:toXScale="0.9" + android:fromYScale="1.0" android:toYScale="0.9" + android:pivotX="50%" android:pivotY="50%" + android:duration="@android:integer/config_shortAnimTime" /> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:duration="@android:integer/config_shortAnimTime"/> </set> diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml index 98975fb..db331b1 100644 --- a/core/res/res/anim/task_open_exit.xml +++ b/core/res/res/anim/task_open_exit.xml @@ -18,8 +18,7 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:interpolator="@anim/decelerate_interpolator" - android:zAdjustment="top"> + android:interpolator="@anim/decelerate_interpolator"> <scale android:fromXScale="1.0" android:toXScale="2.0" android:fromYScale="1.0" android:toYScale="2.0" android:pivotX="50%p" android:pivotY="50%p" diff --git a/core/res/res/anim/translucent_enter.xml b/core/res/res/anim/translucent_enter.xml index fb4c1c3..04852a8 100644 --- a/core/res/res/anim/translucent_enter.xml +++ b/core/res/res/anim/translucent_enter.xml @@ -20,7 +20,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator"> <translate android:fromXDelta="75%" android:toXDelta="0" - android:duration="@android:integer/config_mediumAnimTime"/> + android:duration="@android:integer/config_shortAnimTime"/> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" - android:duration="@android:integer/config_mediumAnimTime"/> + android:duration="@android:integer/config_shortAnimTime"/> </set> diff --git a/core/res/res/anim/translucent_exit.xml b/core/res/res/anim/translucent_exit.xml index 1d424e1..adaf3d1 100644 --- a/core/res/res/anim/translucent_exit.xml +++ b/core/res/res/anim/translucent_exit.xml @@ -20,7 +20,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/accelerate_interpolator"> <translate android:fromXDelta="0%" android:toXDelta="75%" - android:duration="@android:integer/config_mediumAnimTime"/> + android:duration="@android:integer/config_shortAnimTime"/> <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:duration="@android:integer/config_mediumAnimTime"/> + android:duration="@android:integer/config_shortAnimTime"/> </set> diff --git a/core/res/res/anim/wallpaper_close_enter.xml b/core/res/res/anim/wallpaper_close_enter.xml index e4c7e9b..0d13009 100644 --- a/core/res/res/anim/wallpaper_close_enter.xml +++ b/core/res/res/anim/wallpaper_close_enter.xml @@ -17,8 +17,22 @@ */ --> +<!-- This version zooms the new non-wallpaper down on top of the + wallpaper. --> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@anim/decelerate_interpolator"> + <scale android:fromXScale="2.0" android:toXScale="1.0" + android:fromYScale="2.0" android:toYScale="1.0" + android:pivotX="50%p" android:pivotY="50%p" + android:duration="@android:integer/config_mediumAnimTime" /> +</set> + +<!-- This version is a variation on the inter-activity slide that + also scales the wallpaper. --> +<!-- <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator"> <translate android:fromXDelta="33%" android:toXDelta="0" android:duration="@android:integer/config_mediumAnimTime"/> </set> +--> diff --git a/core/res/res/anim/wallpaper_close_exit.xml b/core/res/res/anim/wallpaper_close_exit.xml index 16edec1..5d91e30 100644 --- a/core/res/res/anim/wallpaper_close_exit.xml +++ b/core/res/res/anim/wallpaper_close_exit.xml @@ -17,6 +17,22 @@ */ --> +<!-- This version zooms the new non-wallpaper down on top of the + wallpaper. The wallpaper here just stays fixed behind. --> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@anim/decelerate_interpolator" + android:zAdjustment="top"> + <scale android:fromXScale="1.0" android:toXScale=".5" + android:fromYScale="1.0" android:toYScale=".5" + android:pivotX="50%p" android:pivotY="50%p" + android:duration="@android:integer/config_mediumAnimTime" /> + <alpha android:fromAlpha="1.0" android:toAlpha="0" + android:duration="@android:integer/config_mediumAnimTime"/> +</set> + +<!-- This version is a variation on the inter-activity slide that + also scales the wallpaper. --> +<!-- <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator" android:zAdjustment="top"> @@ -27,3 +43,4 @@ <translate android:fromXDelta="0%" android:toXDelta="-100%" android:duration="@android:integer/config_mediumAnimTime"/> </set> +--> diff --git a/core/res/res/anim/wallpaper_open_enter.xml b/core/res/res/anim/wallpaper_open_enter.xml index af22b47..cf27cf0 100644 --- a/core/res/res/anim/wallpaper_open_enter.xml +++ b/core/res/res/anim/wallpaper_open_enter.xml @@ -17,6 +17,22 @@ */ --> +<!-- This version zooms the new non-wallpaper up off the wallpaper the + wallpaper. The wallpaper here just stays fixed behind. --> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@anim/decelerate_interpolator" + android:zAdjustment="top"> + <scale android:fromXScale=".5" android:toXScale="1.0" + android:fromYScale=".5" android:toYScale="1.0" + android:pivotX="50%p" android:pivotY="50%p" + android:duration="@android:integer/config_mediumAnimTime" /> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:duration="@android:integer/config_mediumAnimTime"/> +</set> + +<!-- This version is a variation on the inter-activity slide that + also scales the wallpaper. --> +<!-- <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator" android:zAdjustment="top"> @@ -27,3 +43,4 @@ <translate android:fromXDelta="-100%" android:toXDelta="0" android:duration="@android:integer/config_mediumAnimTime"/> </set> +--> diff --git a/core/res/res/anim/wallpaper_open_exit.xml b/core/res/res/anim/wallpaper_open_exit.xml index 47cb6d6..b7a539c 100644 --- a/core/res/res/anim/wallpaper_open_exit.xml +++ b/core/res/res/anim/wallpaper_open_exit.xml @@ -17,8 +17,22 @@ */ --> +<!-- This version zooms the new non-wallpaper down on top of the + wallpaper. --> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@anim/decelerate_interpolator"> + <scale android:fromXScale="1.0" android:toXScale="2.0" + android:fromYScale="1.0" android:toYScale="2.0" + android:pivotX="50%p" android:pivotY="50%p" + android:duration="@android:integer/config_mediumAnimTime" /> +</set> + +<!-- This version is a variation on the inter-activity slide that + also scales the wallpaper. --> +<!-- <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@anim/decelerate_interpolator"> <translate android:fromXDelta="0%" android:toXDelta="33%" android:duration="@android:integer/config_mediumAnimTime"/> </set> +--> diff --git a/core/res/res/drawable-hdpi/dark_header.9.png b/core/res/res/drawable-hdpi/dark_header.9.png Binary files differindex a2fa569..3e63fa6 100644 --- a/core/res/res/drawable-hdpi/dark_header.9.png +++ b/core/res/res/drawable-hdpi/dark_header.9.png diff --git a/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png b/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png Binary files differindex c7803a2..99a67b9 100644 --- a/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png +++ b/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png diff --git a/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png b/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png Binary files differindex d8d8aa9..cfe258b 100644 --- a/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png +++ b/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png Binary files differindex 63859f7..30a68d0 100644 --- a/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png +++ b/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png Binary files differindex ced2832..8f35315 100644 --- a/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png +++ b/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png diff --git a/core/res/res/drawable-hdpi/divider_vertical_bright.9.png b/core/res/res/drawable-hdpi/divider_vertical_bright.9.png Binary files differindex 1035656..99a67b9 100644 --- a/core/res/res/drawable-hdpi/divider_vertical_bright.9.png +++ b/core/res/res/drawable-hdpi/divider_vertical_bright.9.png diff --git a/core/res/res/drawable/divider_vertical_bright_opaque.9.png b/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png Binary files differindex 5c537ee..5c537ee 100644 --- a/core/res/res/drawable/divider_vertical_bright_opaque.9.png +++ b/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png diff --git a/core/res/res/drawable-hdpi/divider_vertical_dark.9.png b/core/res/res/drawable-hdpi/divider_vertical_dark.9.png Binary files differnew file mode 100644 index 0000000..30a68d0 --- /dev/null +++ b/core/res/res/drawable-hdpi/divider_vertical_dark.9.png diff --git a/core/res/res/drawable/divider_vertical_dark_opaque.9.png b/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png Binary files differindex 8f35315..8f35315 100644 --- a/core/res/res/drawable/divider_vertical_dark_opaque.9.png +++ b/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png diff --git a/core/res/res/drawable-hdpi/fasttrack_badge.9.png b/core/res/res/drawable-hdpi/fasttrack_badge.9.png Binary files differnew file mode 100644 index 0000000..1eeabf4 --- /dev/null +++ b/core/res/res/drawable-hdpi/fasttrack_badge.9.png diff --git a/core/res/res/drawable-hdpi/fasttrack_badge_pressed.9.png b/core/res/res/drawable-hdpi/fasttrack_badge_pressed.9.png Binary files differnew file mode 100644 index 0000000..0cfd09d --- /dev/null +++ b/core/res/res/drawable-hdpi/fasttrack_badge_pressed.9.png diff --git a/core/res/res/drawable-hdpi/fasttrack_badge_small.9.png b/core/res/res/drawable-hdpi/fasttrack_badge_small.9.png Binary files differnew file mode 100644 index 0000000..7140957 --- /dev/null +++ b/core/res/res/drawable-hdpi/fasttrack_badge_small.9.png diff --git a/core/res/res/drawable-hdpi/fasttrack_badge_small_pressed.9.png b/core/res/res/drawable-hdpi/fasttrack_badge_small_pressed.9.png Binary files differnew file mode 100644 index 0000000..ee030fb --- /dev/null +++ b/core/res/res/drawable-hdpi/fasttrack_badge_small_pressed.9.png diff --git a/core/res/res/drawable-hdpi/ic_aggregated.png b/core/res/res/drawable-hdpi/ic_aggregated.png Binary files differnew file mode 100644 index 0000000..7ca15b1 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_aggregated.png diff --git a/core/res/res/drawable-hdpi/ic_lock_ringer_off.png b/core/res/res/drawable-hdpi/ic_lock_ringer_off.png Binary files differnew file mode 100644 index 0000000..e7cb234 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lock_ringer_off.png diff --git a/core/res/res/drawable-hdpi/ic_lock_ringer_on.png b/core/res/res/drawable-hdpi/ic_lock_ringer_on.png Binary files differnew file mode 100644 index 0000000..ce0cfab --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lock_ringer_on.png diff --git a/core/res/res/drawable-hdpi/light_header.9.png b/core/res/res/drawable-hdpi/light_header.9.png Binary files differindex 27db59d..6fc53ca 100644 --- a/core/res/res/drawable-hdpi/light_header.9.png +++ b/core/res/res/drawable-hdpi/light_header.9.png diff --git a/core/res/res/drawable-mdpi/dark_header.9.png b/core/res/res/drawable-mdpi/dark_header.9.png Binary files differindex 7242b61..f4a14f1 100644 --- a/core/res/res/drawable-mdpi/dark_header.9.png +++ b/core/res/res/drawable-mdpi/dark_header.9.png diff --git a/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png b/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png Binary files differnew file mode 100644 index 0000000..5c537ee --- /dev/null +++ b/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png diff --git a/core/res/res/drawable/divider_vertical_dark.9.png b/core/res/res/drawable-mdpi/divider_vertical_dark.9.png Binary files differindex 548d0bd..548d0bd 100644 --- a/core/res/res/drawable/divider_vertical_dark.9.png +++ b/core/res/res/drawable-mdpi/divider_vertical_dark.9.png diff --git a/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png b/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png Binary files differnew file mode 100644 index 0000000..8f35315 --- /dev/null +++ b/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png diff --git a/core/res/res/drawable-mdpi/fasttrack_badge.9.png b/core/res/res/drawable-mdpi/fasttrack_badge.9.png Binary files differnew file mode 100644 index 0000000..d8dff34 --- /dev/null +++ b/core/res/res/drawable-mdpi/fasttrack_badge.9.png diff --git a/core/res/res/drawable-mdpi/fasttrack_badge_pressed.9.png b/core/res/res/drawable-mdpi/fasttrack_badge_pressed.9.png Binary files differnew file mode 100644 index 0000000..c8ca33a --- /dev/null +++ b/core/res/res/drawable-mdpi/fasttrack_badge_pressed.9.png diff --git a/core/res/res/drawable-mdpi/fasttrack_badge_small.9.png b/core/res/res/drawable-mdpi/fasttrack_badge_small.9.png Binary files differnew file mode 100644 index 0000000..38f14f7 --- /dev/null +++ b/core/res/res/drawable-mdpi/fasttrack_badge_small.9.png diff --git a/core/res/res/drawable-mdpi/fasttrack_badge_small_pressed.9.png b/core/res/res/drawable-mdpi/fasttrack_badge_small_pressed.9.png Binary files differnew file mode 100644 index 0000000..b23e921 --- /dev/null +++ b/core/res/res/drawable-mdpi/fasttrack_badge_small_pressed.9.png diff --git a/core/res/res/drawable-mdpi/ic_aggregated.png b/core/res/res/drawable-mdpi/ic_aggregated.png Binary files differnew file mode 100644 index 0000000..7c2e2b0 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_aggregated.png diff --git a/core/res/res/drawable-mdpi/ic_lock_ringer_off.png b/core/res/res/drawable-mdpi/ic_lock_ringer_off.png Binary files differnew file mode 100644 index 0000000..98cfb11 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lock_ringer_off.png diff --git a/core/res/res/drawable-mdpi/ic_lock_ringer_on.png b/core/res/res/drawable-mdpi/ic_lock_ringer_on.png Binary files differnew file mode 100644 index 0000000..691b99e --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lock_ringer_on.png diff --git a/core/res/res/drawable/fasttrack_badge_dark.xml b/core/res/res/drawable/fasttrack_badge.xml index c60d403..89c63a1 100644 --- a/core/res/res/drawable/fasttrack_badge_dark.xml +++ b/core/res/res/drawable/fasttrack_badge.xml @@ -19,10 +19,10 @@ android:state_focused="false" android:state_selected="false" android:state_pressed="false" - android:drawable="@drawable/fasttrack_badge_dark_normal" /> + android:drawable="@drawable/fasttrack_badge" /> <item android:state_pressed="true" - android:drawable="@drawable/fasttrack_badge_dark_pressed" /> + android:drawable="@drawable/fasttrack_badge_pressed" /> </selector> diff --git a/core/res/res/drawable/fasttrack_badge_dark_normal.9.png b/core/res/res/drawable/fasttrack_badge_dark_normal.9.png Binary files differdeleted file mode 100644 index 52bb08c..0000000 --- a/core/res/res/drawable/fasttrack_badge_dark_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable/fasttrack_badge_dark_pressed.9.png b/core/res/res/drawable/fasttrack_badge_dark_pressed.9.png Binary files differdeleted file mode 100644 index 84a6783..0000000 --- a/core/res/res/drawable/fasttrack_badge_dark_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable/fasttrack_badge_light_normal.9.png b/core/res/res/drawable/fasttrack_badge_light_normal.9.png Binary files differdeleted file mode 100644 index 595b179..0000000 --- a/core/res/res/drawable/fasttrack_badge_light_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable/fasttrack_badge_light_pressed.9.png b/core/res/res/drawable/fasttrack_badge_light_pressed.9.png Binary files differdeleted file mode 100644 index 8e3f557..0000000 --- a/core/res/res/drawable/fasttrack_badge_light_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable/fasttrack_badge_middle_large.xml b/core/res/res/drawable/fasttrack_badge_middle_large.xml deleted file mode 100644 index dd591bd..0000000 --- a/core/res/res/drawable/fasttrack_badge_middle_large.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2009 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:state_focused="false" - android:state_selected="false" - android:state_pressed="false" - android:drawable="@drawable/fasttrack_badge_middle_large_normal" /> - - <item - android:state_pressed="true" - android:drawable="@drawable/fasttrack_badge_middle_large_pressed" /> - -</selector>
\ No newline at end of file diff --git a/core/res/res/drawable/fasttrack_badge_middle_large_normal.9.png b/core/res/res/drawable/fasttrack_badge_middle_large_normal.9.png Binary files differdeleted file mode 100644 index ca275cd..0000000 --- a/core/res/res/drawable/fasttrack_badge_middle_large_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable/fasttrack_badge_middle_large_pressed.9.png b/core/res/res/drawable/fasttrack_badge_middle_large_pressed.9.png Binary files differdeleted file mode 100644 index b69ccbd..0000000 --- a/core/res/res/drawable/fasttrack_badge_middle_large_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable/fasttrack_badge_light.xml b/core/res/res/drawable/fasttrack_badge_small.xml index fd81258..269e936 100644 --- a/core/res/res/drawable/fasttrack_badge_light.xml +++ b/core/res/res/drawable/fasttrack_badge_small.xml @@ -19,10 +19,10 @@ android:state_focused="false" android:state_selected="false" android:state_pressed="false" - android:drawable="@drawable/fasttrack_badge_light_normal" /> + android:drawable="@drawable/fasttrack_badge_small" /> <item android:state_pressed="true" - android:drawable="@drawable/fasttrack_badge_light_pressed" /> + android:drawable="@drawable/fasttrack_badge_small_pressed" /> </selector> diff --git a/core/res/res/layout/contact_header.xml b/core/res/res/layout/contact_header.xml index e800dfa..d19bb04 100644 --- a/core/res/res/layout/contact_header.xml +++ b/core/res/res/layout/contact_header.xml @@ -38,8 +38,24 @@ android:layout_marginTop="5dip" android:orientation="vertical"> - <!-- "Name" field is locale-specific. --> - <include layout="@layout/contact_header_name"/> + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <ImageView + android:id="@+id/aggregate_badge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="3dip" + android:paddingTop="3dip" + android:src="@drawable/ic_aggregated" + /> + + <!-- "Name" field is locale-specific. --> + <include layout="@layout/contact_header_name"/> + + </LinearLayout> <TextView android:id="@+id/status" android:layout_width="fill_parent" diff --git a/core/res/res/layout/keyguard_screen_rotary_unlock.xml b/core/res/res/layout/keyguard_screen_rotary_unlock.xml index cf97d04..9f18124 100644 --- a/core/res/res/layout/keyguard_screen_rotary_unlock.xml +++ b/core/res/res/layout/keyguard_screen_rotary_unlock.xml @@ -83,6 +83,7 @@ android:layout_marginTop="6dip" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="?android:attr/textColorSecondary" + android:drawablePadding="4dip" /> <TextView @@ -94,6 +95,7 @@ android:layout_marginTop="6dip" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="?android:attr/textColorSecondary" + android:drawablePadding="4dip" /> <TextView @@ -108,15 +110,15 @@ android:layout_marginTop="12dip" /> - <!-- By having the rotary selector hang below "screen locked" text, we get a layout more - robust for different screen sizes. On wvga, the widget should be flush with the bottom.--> + <!-- By having the rotary selector hang from the top, we get a layout more + robust for different screen sizes. On hvga, the widget should be flush with the bottom.--> <com.android.internal.widget.RotarySelector android:id="@+id/rotary" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_below="@id/screenLocked" android:layout_centerHorizontal="true" - android:layout_marginTop="24dip" + android:layout_alignParentTop="true" + android:layout_marginTop="286dip" /> <!-- emergency call button shown when sim is missing or PUKd --> diff --git a/core/res/res/layout/tab_indicator.xml b/core/res/res/layout/tab_indicator.xml index e3ea555..71e4001 100644 --- a/core/res/res/layout/tab_indicator.xml +++ b/core/res/res/layout/tab_indicator.xml @@ -18,8 +18,8 @@ android:layout_width="0dip" android:layout_height="64dip" android:layout_weight="1" - android:layout_marginLeft="-4px" - android:layout_marginRight="-4px" + android:layout_marginLeft="-3dip" + android:layout_marginRight="-3dip" android:orientation="vertical" android:background="@android:drawable/tab_indicator"> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index eae838a..a0d046f 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -102,9 +102,9 @@ <!-- Text color, typeface, size, and style for "small" inverse text. Defaults to secondary inverse text color. --> <attr name="textAppearanceSmallInverse" format="reference" /> - <!-- Text color, typeface, size, and style for system search result title. Defaults to primary inverse text color. @hide --> + <!-- Text color, typeface, size, and style for system search result title. Defaults to primary inverse text color. --> <attr name="textAppearanceSearchResultTitle" format="reference" /> - <!-- Text color, typeface, size, and style for system search result subtitle. Defaults to primary inverse text color. @hide --> + <!-- Text color, typeface, size, and style for system search result subtitle. Defaults to primary inverse text color. --> <attr name="textAppearanceSearchResultSubtitle" format="reference" /> @@ -396,14 +396,20 @@ <attr name="spinnerItemStyle" format="reference" /> <!-- Default MapView style. --> <attr name="mapViewStyle" format="reference" /> - <!-- Dark Fasttrack badge style. --> + <!-- Default Fasttrack badge style. --> <attr name="fasttrackBadgeWidgetStyle" format="reference" /> - <!-- Dark Fasttrack badge style with small fasttrack window. --> + <!-- Default Fasttrack badge style with small fasttrack window. --> <attr name="fasttrackBadgeWidgetStyleWindowSmall" format="reference" /> - <!-- Dark Fasttrack badge style with medium fasttrack window. --> + <!-- Default Fasttrack badge style with medium fasttrack window. --> <attr name="fasttrackBadgeWidgetStyleWindowMedium" format="reference" /> - <!-- Dark Fasttrack badge style with large fasttrack window. --> + <!-- Default Fasttrack badge style with large fasttrack window. --> <attr name="fasttrackBadgeWidgetStyleWindowLarge" format="reference" /> + <!-- Default Fasttrack badge style with small fasttrack window. --> + <attr name="fasttrackBadgeWidgetStyleSmallWindowSmall" format="reference" /> + <!-- Default Fasttrack badge style with medium fasttrack window. --> + <attr name="fasttrackBadgeWidgetStyleSmallWindowMedium" format="reference" /> + <!-- Default Fasttrack badge style with large fasttrack window. --> + <attr name="fasttrackBadgeWidgetStyleSmallWindowLarge" format="reference" /> <!-- =================== --> <!-- Preference styles --> @@ -3486,13 +3492,29 @@ <!-- Contacts meta-data attributes --> <!-- =============================== --> + <!-- TODO: remove this deprecated styleable --> <declare-styleable name="Icon"> <attr name="icon" /> <attr name="mimeType" /> </declare-styleable> + <!-- TODO: remove this deprecated styleable --> <declare-styleable name="IconDefault"> <attr name="icon" /> </declare-styleable> + <!-- Maps a specific contact data MIME-type to styling information --> + <declare-styleable name="ContactsDataKind"> + <!-- Mime-type handled by this mapping --> + <attr name="mimeType" /> + <!-- Icon used to represent data of this kind --> + <attr name="icon" /> + <!-- Column in data table that summarizes this data --> + <attr name="summaryColumn" format="string" /> + <!-- Column in data table that contains details for this data --> + <attr name="detailColumn" format="string" /> + <!-- Flag indicating that detail should be built from SocialProvider --> + <attr name="detailSocialSummary" format="boolean" /> + </declare-styleable> + </resources> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index c967c4c..15841a8 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -19,7 +19,7 @@ --> <resources> <drawable name="screen_background_light">#ffffffff</drawable> - <drawable name="screen_background_dark">#ff202020</drawable> + <drawable name="screen_background_dark">#ff000000</drawable> <drawable name="status_bar_closed_default_background">#ff000000</drawable> <drawable name="status_bar_opened_default_background">#ff000000</drawable> <drawable name="search_bar_default_color">#ff000000</drawable> @@ -36,7 +36,7 @@ <color name="white">#ffffffff</color> <color name="black">#ff000000</color> <color name="transparent">#00000000</color> - <color name="background_dark">#ff202020</color> + <color name="background_dark">#ff000000</color> <color name="bright_foreground_dark">#ffffffff</color> <color name="bright_foreground_dark_disabled">#80ffffff</color> <color name="bright_foreground_dark_inverse">#ff000000</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b710acb..7aeaec4 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -27,13 +27,13 @@ <bool name="config_sf_limitedAlpha">false</bool> <!-- The duration (in milliseconds) of a short animation. --> - <integer name="config_shortAnimTime">100</integer> + <integer name="config_shortAnimTime">150</integer> <!-- The duration (in milliseconds) of a medium-length animation. --> - <integer name="config_mediumAnimTime">150</integer> + <integer name="config_mediumAnimTime">250</integer> <!-- The duration (in milliseconds) of a long animation. --> - <integer name="config_longAnimTime">300</integer> + <integer name="config_longAnimTime">400</integer> <!-- XXXXX NOTE THE FOLLOWING RESOURCES USE THE WRONG NAMING CONVENTION. Please don't copy them, copy anything else. --> @@ -78,6 +78,20 @@ A value of -1 means no change in orientation by default. --> <integer name="config_carDockRotation">-1</integer> + <!-- Control whether being in the desk dock (and powered) always + keeps the screen on. By default it doesn't. Set to true to make it. --> + <bool name="config_deskDockKeepsScreenOn">false</bool> + + <!-- Control whether being in the car dock (and powered) always + keeps the screen on. By default it does. Set to false to not keep on. --> + <bool name="config_carDockKeepsScreenOn">true</bool> + + <!-- Control whether being in the desk dock should enable accelerometer based screen orientation --> + <bool name="config_deskDockEnablesAccelerometer">false</bool> + + <!-- Control whether being in the car dock should enable accelerometer based screen orientation --> + <bool name="config_carDockEnablesAccelerometer">false</bool> + <!-- Indicate whether the lid state impacts the accessibility of the physical keyboard. 0 means it doesn't, 1 means it is accessible when the lid is open, 2 means it is accessible when the lid is @@ -105,5 +119,7 @@ <item>20</item> <item>30</item> </integer-array> - + + <bool name="config_use_strict_phone_number_comparation">false</bool> + </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 2bc2a0f..b08a58a 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1164,12 +1164,19 @@ <public type="attr" name="restoreNeedsApplication" /> <public type="attr" name="smallIcon" /> <public type="attr" name="accountPreferences" /> + <public type="attr" name="textAppearanceSearchResultSubtitle" /> + <public type="attr" name="textAppearanceSearchResultTitle" /> + <public type="attr" name="summaryColumn" /> + <public type="attr" name="detailColumn" /> + <public type="attr" name="detailSocialSummary" /> <public type="style" name="Theme.Wallpaper" /> <public type="style" name="Theme.Wallpaper.NoTitleBar" /> <public type="style" name="Theme.Wallpaper.NoTitleBar.Fullscreen" /> <public type="style" name="Theme.WallpaperSettings" /> <public type="style" name="Theme.Light.WallpaperSettings" /> + <public type="style" name="TextAppearance.SearchResult.Title" /> + <public type="style" name="TextAppearance.SearchResult.Subtitle" /> <!-- Semi-transparent background that can be used when placing a dark themed UI on top of some arbitrary background (such as the diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index bd79c75..e2f6981 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1200,6 +1200,106 @@ <item>Jabber</item> </string-array> + <!-- Custom phone number type --> + <string name="phoneTypeCustom">Custom</string> + <!-- Home phone number type --> + <string name="phoneTypeHome">Home</string> + <!-- Mobile phone number type --> + <string name="phoneTypeMobile">Mobile</string> + <!-- Work phone number type --> + <string name="phoneTypeWork">Work</string> + <!-- Work fax phone number type --> + <string name="phoneTypeFaxWork">Work Fax</string> + <!-- Home fax phone number type --> + <string name="phoneTypeFaxHome">Home Fax</string> + <!-- Pager phone number type --> + <string name="phoneTypePager">Pager</string> + <!-- Other phone number type --> + <string name="phoneTypeOther">Other</string> + <!-- Callback phone number type --> + <string name="phoneTypeCallback">Callback</string> + <!-- Car phone number type --> + <string name="phoneTypeCar">Car</string> + <!-- Company main phone number type --> + <string name="phoneTypeCompanyMain">Company Main</string> + <!-- ISDN phone number type --> + <string name="phoneTypeIsdn">ISDN</string> + <!-- Main phone number type --> + <string name="phoneTypeMain">Main</string> + <!-- Other fax phone number type --> + <string name="phoneTypeOtherFax">Other Fax</string> + <!-- Radio phone number type --> + <string name="phoneTypeRadio">Radio</string> + <!-- Telex phone number type --> + <string name="phoneTypeTelex">Telex</string> + <!-- TTY TDD phone number type --> + <string name="phoneTypeTtyTdd">TTY TDD</string> + <!-- Work mobile phone number type --> + <string name="phoneTypeWorkMobile">Work Mobile</string> + <!-- Work pager phone number type --> + <string name="phoneTypeWorkPager">Work Pager</string> + <!-- Assistant phone number type --> + <string name="phoneTypeAssistant">Assistant</string> + <!-- MMS phone number type --> + <string name="phoneTypeMms">MMS</string> + + <!-- Custom email type --> + <string name="emailTypeCustom">Custom</string> + <!-- Home email type --> + <string name="emailTypeHome">Home</string> + <!-- Work email type --> + <string name="emailTypeWork">Work</string> + <!-- Other email type --> + <string name="emailTypeOther">Other</string> + <!-- Mobile email type --> + <string name="emailTypeMobile">Mobile</string> + + <!-- Custom postal address type --> + <string name="postalTypeCustom">Custom</string> + <!-- Home postal address type --> + <string name="postalTypeHome">Home</string> + <!-- Work postal address type --> + <string name="postalTypeWork">Work</string> + <!-- Other postal address type --> + <string name="postalTypeOther">Other</string> + + <!-- Custom IM address type --> + <string name="imTypeCustom">Custom</string> + <!-- Home IM address type --> + <string name="imTypeHome">Home</string> + <!-- Work IM address type --> + <string name="imTypeWork">Work</string> + <!-- Other IM address type --> + <string name="imTypeOther">Other</string> + + <!-- Custom IM address type --> + <string name="imProtocolCustom">Custom</string> + <!-- AIM IM protocol type --> + <string name="imProtocolAim">AIM</string> + <!-- MSN IM protocol type --> + <string name="imProtocolMsn">Windows Live</string> + <!-- Yahoo IM protocol type --> + <string name="imProtocolYahoo">Yahoo</string> + <!-- Skype IM protocol type --> + <string name="imProtocolSkype">Skype</string> + <!-- QQ IM protocol type --> + <string name="imProtocolQq">QQ</string> + <!-- Google Talk IM protocol type --> + <string name="imProtocolGoogleTalk">Google Talk</string> + <!-- ICQ IM protocol type --> + <string name="imProtocolIcq">ICQ</string> + <!-- Jabber IM protocol type --> + <string name="imProtocolJabber">Jabber</string> + <!-- NetMeeting IM protocol type --> + <string name="imProtocolNetMeeting">NetMeeting</string> + + <!-- Work organization type --> + <string name="orgTypeWork">Work</string> + <!-- Other organization type --> + <string name="orgTypeOther">Other</string> + <!-- Custom organization type --> + <string name="orgTypeCustom">Custom</string> + <!-- Instructions telling the user to enter their pin to unlock the keyguard. Displayed in one line in a large font. --> <string name="keyguard_password_enter_pin_code">Enter PIN code</string> @@ -1391,6 +1491,9 @@ <!-- Title of the WebView save password dialog. 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. --> <string name="save_password_label">Confirm</string> + <!-- Toast for double-tap --> + <string name="double_tap_toast">Tip: double-tap to zoom in and out.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 35db8ee..e78c213 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -58,6 +58,19 @@ <item name="activityOpenExitAnimation">@anim/activity_open_exit</item> <item name="activityCloseEnterAnimation">@anim/activity_close_enter</item> <item name="activityCloseExitAnimation">@anim/activity_close_exit</item> + <item name="taskOpenEnterAnimation">@anim/activity_open_enter</item> + <item name="taskOpenExitAnimation">@anim/activity_open_exit</item> + <item name="taskCloseEnterAnimation">@anim/activity_close_enter</item> + <item name="taskCloseExitAnimation">@anim/activity_close_exit</item> + <item name="taskToFrontEnterAnimation">@anim/activity_open_enter</item> + <item name="taskToFrontExitAnimation">@anim/activity_open_exit</item> + <item name="taskToBackEnterAnimation">@anim/activity_close_enter</item> + <item name="taskToBackExitAnimation">@anim/activity_close_exit</item> + <!-- There is a good argument to be made that the user shouldn't + be aware of task transitions, so we are going to use the same + animation for them as we do for regular activity transitions. --> + <!-- These provide an alternative animation for task transitions. --> + <!-- <item name="taskOpenEnterAnimation">@anim/task_open_enter</item> <item name="taskOpenExitAnimation">@anim/task_open_exit</item> <item name="taskCloseEnterAnimation">@anim/task_close_enter</item> @@ -66,6 +79,7 @@ <item name="taskToFrontExitAnimation">@anim/task_open_exit</item> <item name="taskToBackEnterAnimation">@anim/task_close_enter</item> <item name="taskToBackExitAnimation">@anim/task_close_exit</item> + --> <item name="wallpaperOpenEnterAnimation">@anim/wallpaper_open_enter</item> <item name="wallpaperOpenExitAnimation">@anim/wallpaper_open_exit</item> <item name="wallpaperCloseEnterAnimation">@anim/wallpaper_close_enter</item> @@ -524,9 +538,17 @@ </style> <style name="Widget.FasttrackBadgeWidget"> - <item name="android:layout_width">48dip</item> - <item name="android:layout_height">52dip</item> - <item name="android:background">@android:drawable/fasttrack_badge_dark</item> + <item name="android:layout_width">50dip</item> + <item name="android:layout_height">56dip</item> + <item name="android:background">@android:drawable/fasttrack_badge</item> + <item name="android:clickable">true</item> + <item name="android:scaleType">fitCenter</item> + </style> + + <style name="Widget.FasttrackBadgeWidgetSmall"> + <item name="android:layout_width">39dip</item> + <item name="android:layout_height">42dip</item> + <item name="android:background">@android:drawable/fasttrack_badge_small</item> <item name="android:clickable">true</item> <item name="android:scaleType">fitCenter</item> </style> @@ -542,6 +564,18 @@ <style name="Widget.FasttrackBadgeWidget.WindowLarge"> <item name="android:fasttrackWindowSize">modeLarge</item> </style> + + <style name="Widget.FasttrackBadgeWidgetSmall.WindowSmall"> + <item name="android:fasttrackWindowSize">modeSmall</item> + </style> + + <style name="Widget.FasttrackBadgeWidgetSmall.WindowMedium"> + <item name="android:fasttrackWindowSize">modeMedium</item> + </style> + + <style name="Widget.FasttrackBadgeWidgetSmall.WindowLarge"> + <item name="android:fasttrackWindowSize">modeLarge</item> + </style> <!-- Text Appearances --> <eat-comment /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 04402fd..c0ca21b 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -59,11 +59,7 @@ <item name="textAppearanceLargeInverse">@android:style/TextAppearance.Large.Inverse</item> <item name="textAppearanceMediumInverse">@android:style/TextAppearance.Medium.Inverse</item> <item name="textAppearanceSmallInverse">@android:style/TextAppearance.Small.Inverse</item> - - <!-- @hide --> <item name="textAppearanceSearchResultTitle">@android:style/TextAppearance.SearchResult.Title</item> - - <!-- @hide --> <item name="textAppearanceSearchResultSubtitle">@android:style/TextAppearance.SearchResult.Subtitle</item> <item name="textAppearanceButton">@android:style/TextAppearance.Widget.Button</item> @@ -179,6 +175,9 @@ <item name="fasttrackBadgeWidgetStyleWindowSmall">@android:style/Widget.FasttrackBadgeWidget.WindowSmall</item> <item name="fasttrackBadgeWidgetStyleWindowMedium">@android:style/Widget.FasttrackBadgeWidget.WindowMedium</item> <item name="fasttrackBadgeWidgetStyleWindowLarge">@android:style/Widget.FasttrackBadgeWidget.WindowLarge</item> + <item name="fasttrackBadgeWidgetStyleSmallWindowSmall">@android:style/Widget.FasttrackBadgeWidgetSmall.WindowSmall</item> + <item name="fasttrackBadgeWidgetStyleSmallWindowMedium">@android:style/Widget.FasttrackBadgeWidgetSmall.WindowMedium</item> + <item name="fasttrackBadgeWidgetStyleSmallWindowLarge">@android:style/Widget.FasttrackBadgeWidgetSmall.WindowLarge</item> <!-- Preference styles --> <item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item> @@ -358,7 +357,7 @@ <style name="Theme.NoDisplay"> <item name="android:windowBackground">@null</item> <item name="android:windowContentOverlay">@null</item> - <item name="android:windowIsTranslucent">false</item> + <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@null</item> <item name="android:windowDisablePreview">true</item> <item name="android:windowNoDisplay">true</item> diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml index 859902e..710b71e 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -26,15 +26,24 @@ <item name="wifi.on">0.1</item> <item name="wifi.active">0.1</item> <item name="wifi.scan">0.1</item> - <item name="cpu.idle">0.1</item> - <item name="cpu.normal">0.2</item> - <item name="cpu.full">1</item> <item name="dsp.audio">0.1</item> <item name="dsp.video">0.1</item> <item name="radio.active">1</item> <item name="gps.on">1</item> + <!-- Current consumed by the radio at different signal strengths, when paging --> <array name="radio.on"> <!-- Strength 0 to BINS-1 --> <value>1</value> <value>0.1</value> </array> + <!-- Different CPU speeds as reported in + /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state --> + <array name="cpu.speeds"> + <value>400000</value> <!-- 400 MHz CPU speed --> + </array> + <!-- Power consumption when CPU is idle --> + <item name="cpu.idle">0.1</item> + <!-- Power consumption at different speeds --> + <array name="cpu.active"> + <value>0.2</value> + </array> </device> diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java index 221c2f8..c76cee7 100644 --- a/graphics/java/android/graphics/PixelFormat.java +++ b/graphics/java/android/graphics/PixelFormat.java @@ -60,6 +60,13 @@ public class PixelFormat */ public static final int YCbCr_420_SP= 0x11; + /** YCbCr format used for images, which uses YUYV (YUY2) encoding format. + * This is an alternative format for camera preview images. Whether this + * format is supported by the camera hardware can be determined by + * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}. + */ + public static final int YCbCr_422_I = 0x14; + /** * Encoded formats. These are not necessarily supported by the hardware. */ diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java index 30c81ab..957f2dd 100644 --- a/graphics/java/android/renderscript/Allocation.java +++ b/graphics/java/android/renderscript/Allocation.java @@ -48,51 +48,50 @@ public class Allocation extends BaseObj { } public void data(int[] d) { - int size; - if(mType != null && mType.mElement != null) { - size = mType.mElement.mSize; - for(int ct=0; ct < mType.mValues.length; ct++) { - if(mType.mValues[ct] != 0) { - size *= mType.mValues[ct]; - } - } - if((d.length * 4) < size) { - throw new IllegalArgumentException("Array too small for allocation type."); - } - Log.e("rs", "Alloc data size=" + size); - mRS.nAllocationData(mID, d, size); - return; - } - mRS.nAllocationData(mID, d, d.length * 4); + subData1D(0, mType.getElementCount(), d); + } + public void data(short[] d) { + subData1D(0, mType.getElementCount(), d); + } + public void data(byte[] d) { + subData1D(0, mType.getElementCount(), d); } - public void data(float[] d) { - int size; - if(mType != null && mType.mElement != null) { - size = mType.mElement.mSize; - for(int ct=0; ct < mType.mValues.length; ct++) { - if(mType.mValues[ct] != 0) { - size *= mType.mValues[ct]; - } - } - if((d.length * 4) < size) { - throw new IllegalArgumentException("Array too small for allocation type."); - } - Log.e("rs", "Alloc data size=" + size); - mRS.nAllocationData(mID, d, size); - return; + subData1D(0, mType.getElementCount(), d); + } + + private void data1DChecks(int off, int count, int len, int dataSize) { + if((off < 0) || (count < 1) || ((off + count) > mType.getElementCount())) { + throw new IllegalArgumentException("Offset or Count out of bounds."); + } + if((len) < dataSize) { + throw new IllegalArgumentException("Array too small for allocation type."); } - mRS.nAllocationData(mID, d, d.length * 4); } public void subData1D(int off, int count, int[] d) { - mRS.nAllocationSubData1D(mID, off, count, d, count * 4); + int dataSize = mType.mElement.getSizeBytes() * count; + data1DChecks(off, count, d.length * 4, dataSize); + mRS.nAllocationSubData1D(mID, off, count, d, dataSize); + } + public void subData1D(int off, int count, short[] d) { + int dataSize = mType.mElement.getSizeBytes() * count; + data1DChecks(off, count, d.length * 2, dataSize); + mRS.nAllocationSubData1D(mID, off, count, d, dataSize); + } + public void subData1D(int off, int count, byte[] d) { + int dataSize = mType.mElement.getSizeBytes() * count; + data1DChecks(off, count, d.length, dataSize); + mRS.nAllocationSubData1D(mID, off, count, d, dataSize); } - public void subData1D(int off, int count, float[] d) { - mRS.nAllocationSubData1D(mID, off, count, d, d.length * 4); + int dataSize = mType.mElement.getSizeBytes() * count; + data1DChecks(off, count, d.length * 4, dataSize); + mRS.nAllocationSubData1D(mID, off, count, d, dataSize); } + + public void subData2D(int xoff, int yoff, int w, int h, int[] d) { mRS.nAllocationSubData2D(mID, xoff, yoff, w, h, d, d.length * 4); } @@ -213,11 +212,15 @@ public class Allocation extends BaseObj { static public Allocation createSized(RenderScript rs, Element e, int count) throws IllegalArgumentException { - int id = rs.nAllocationCreateSized(e.mID, count); + Type.Builder b = new Type.Builder(rs, e); + b.add(Dimension.X, count); + Type t = b.create(); + + int id = rs.nAllocationCreateTyped(t.mID); if(id == 0) { throw new IllegalStateException("Bad element."); } - return new Allocation(id, rs, null); + return new Allocation(id, rs, t); } static public Allocation createFromBitmap(RenderScript rs, Bitmap b, Element dstFmt, boolean genMips) diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java index 04c36fd..0a586c4 100644 --- a/graphics/java/android/renderscript/Element.java +++ b/graphics/java/android/renderscript/Element.java @@ -26,18 +26,40 @@ public class Element extends BaseObj { int mSize; Entry[] mEntries; + int getSizeBytes() { + return mSize; + } + int getComponentCount() { + return mEntries.length; + } + Element.DataType getComponentDataType(int num) { + return mEntries[num].mType; + } + Element.DataKind getComponentDataKind(int num) { + return mEntries[num].mKind; + } + boolean getComponentIsNormalized(int num) { + return mEntries[num].mIsNormalized; + } + int getComponentBits(int num) { + return mEntries[num].mBits; + } + String getComponentName(int num) { + return mEntries[num].mName; + } + static class Entry { - Element mElement; + //Element mElement; Element.DataType mType; Element.DataKind mKind; boolean mIsNormalized; int mBits; String mName; - Entry(Element e, int bits) { - mElement = e; - int mBits = bits; - } + //Entry(Element e, int bits) { + //mElement = e; + //int mBits = bits; + //} Entry(DataType dt, DataKind dk, boolean isNorm, int bits, String name) { mType = dt; @@ -266,14 +288,11 @@ public class Element extends BaseObj { int bits = 0; for (int ct=0; ct < e.mEntries.length; ct++) { Entry en = e.mEntries[ct]; - if(en.mElement != null) { + //if(en.mElement != null) { //rs.nElementAdd(en.mElement.mID); - } else { - int norm = 0; - if (en.mIsNormalized) { - norm = 1; - } - rs.nElementAdd(en.mKind.mID, en.mType.mID, norm, en.mBits, en.mName); + //} else + { + rs.nElementAdd(en.mKind.mID, en.mType.mID, en.mIsNormalized, en.mBits, en.mName); bits += en.mBits; } } @@ -308,11 +327,11 @@ public class Element extends BaseObj { mEntryCount++; } - public Builder add(Element e) throws IllegalArgumentException { - Entry en = new Entry(e, e.mSize * 8); - addEntry(en); - return this; - } + //public Builder add(Element e) throws IllegalArgumentException { + //Entry en = new Entry(e, e.mSize * 8); + //addEntry(en); + //return this; + //} public Builder add(Element.DataType dt, Element.DataKind dk, boolean isNormalized, int bits, String name) { Entry en = new Entry(dt, dk, isNormalized, bits, name); diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java index d35c5e3..5831d13 100644 --- a/graphics/java/android/renderscript/RenderScript.java +++ b/graphics/java/android/renderscript/RenderScript.java @@ -80,7 +80,7 @@ public class RenderScript { native int nFileOpen(byte[] name); native void nElementBegin(); - native void nElementAdd(int kind, int type, int norm, int bits, String s); + native void nElementAdd(int kind, int type, boolean norm, int bits, String s); native int nElementCreate(); native void nTypeBegin(int elementID); @@ -90,17 +90,19 @@ public class RenderScript { native void nTypeSetupFields(Type t, int[] types, int[] bits, Field[] IDs); native int nAllocationCreateTyped(int type); - native int nAllocationCreateSized(int elem, int count); + //native int nAllocationCreateSized(int elem, int count); native int nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp); native int nAllocationCreateFromBitmapBoxed(int dstFmt, boolean genMips, Bitmap bmp); native int nAllocationCreateFromAssetStream(int dstFmt, boolean genMips, int assetStream); native void nAllocationUploadToTexture(int alloc, int baseMioLevel); native void nAllocationUploadToBufferObject(int alloc); - native void nAllocationData(int id, int[] d, int sizeBytes); - native void nAllocationData(int id, float[] d, int sizeBytes); + native void nAllocationSubData1D(int id, int off, int count, int[] d, int sizeBytes); + native void nAllocationSubData1D(int id, int off, int count, short[] d, int sizeBytes); + native void nAllocationSubData1D(int id, int off, int count, byte[] d, int sizeBytes); native void nAllocationSubData1D(int id, int off, int count, float[] d, int sizeBytes); + native void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, int[] d, int sizeBytes); native void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, float[] d, int sizeBytes); native void nAllocationRead(int id, int[] d); diff --git a/graphics/java/android/renderscript/SimpleMesh.java b/graphics/java/android/renderscript/SimpleMesh.java index 5d87654..dc74c61 100644 --- a/graphics/java/android/renderscript/SimpleMesh.java +++ b/graphics/java/android/renderscript/SimpleMesh.java @@ -162,7 +162,6 @@ public class SimpleMesh extends BaseObj { } public SimpleMesh create() { - Log.e("rs", "SimpleMesh create"); SimpleMesh sm = internalCreate(mRS, this); sm.mVertexTypes = new Type[mVertexTypeCount]; for(int ct=0; ct < mVertexTypeCount; ct++) { @@ -177,7 +176,7 @@ public class SimpleMesh extends BaseObj { public static class TriangleMeshBuilder { float mVtxData[]; int mVtxCount; - int mIndexData[]; + short mIndexData[]; int mIndexCount; RenderScript mRS; Element mElement; @@ -191,7 +190,7 @@ public class SimpleMesh extends BaseObj { mVtxCount = 0; mIndexCount = 0; mVtxData = new float[128]; - mIndexData = new int[128]; + mIndexData = new short[128]; mVtxSize = vtxSize; mNorm = norm; mTex = tex; @@ -268,13 +267,13 @@ public class SimpleMesh extends BaseObj { public void addTriangle(int idx1, int idx2, int idx3) { if((mIndexCount + 3) >= mIndexData.length) { - int t[] = new int[mIndexData.length * 2]; + short t[] = new short[mIndexData.length * 2]; System.arraycopy(mIndexData, 0, t, 0, mIndexData.length); mIndexData = t; } - mIndexData[mIndexCount++] = idx1; - mIndexData[mIndexCount++] = idx2; - mIndexData[mIndexCount++] = idx3; + mIndexData[mIndexCount++] = (short)idx1; + mIndexData[mIndexCount++] = (short)idx2; + mIndexData[mIndexCount++] = (short)idx3; } public SimpleMesh create() { @@ -309,10 +308,6 @@ public class SimpleMesh extends BaseObj { vertexAlloc.data(mVtxData); vertexAlloc.uploadToBufferObject(); - // This is safe because length is a pow2 - for(int ct=0; ct < (mIndexCount+1); ct += 2) { - mIndexData[ct >> 1] = mIndexData[ct] | (mIndexData[ct+1] << 16); - } indexAlloc.data(mIndexData); indexAlloc.uploadToBufferObject(); diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java index b6b7adf..df60990 100644 --- a/graphics/java/android/renderscript/Type.java +++ b/graphics/java/android/renderscript/Type.java @@ -23,13 +23,74 @@ import java.lang.reflect.Field; * **/ public class Type extends BaseObj { - Dimension[] mDimensions; - int[] mValues; + int mDimX; + int mDimY; + int mDimZ; + boolean mDimLOD; + boolean mDimFaces; + int mElementCount; Element mElement; + private int mNativeCache; Class mJavaClass; + public int getX() { + return mDimX; + } + public int getY() { + return mDimY; + } + public int getZ() { + return mDimZ; + } + public boolean getLOD() { + return mDimLOD; + } + public boolean getFaces() { + return mDimFaces; + } + public int getElementCount() { + return mElementCount; + } + + void calcElementCount() { + boolean hasLod = getLOD(); + int x = getX(); + int y = getY(); + int z = getZ(); + int faces = 1; + if(getFaces()) { + faces = 6; + } + if(x == 0) { + x = 1; + } + if(y == 0) { + y = 1; + } + if(z == 0) { + z = 1; + } + + int count = x * y * z * faces; + if(hasLod && (x > 1) && (y > 1) && (z > 1)) { + if(x > 1) { + x >>= 1; + } + if(y > 1) { + y >>= 1; + } + if(z > 1) { + z >>= 1; + } + + count += x * y * z * faces; + } + mElementCount = count; + } + + Type(int id, RenderScript rs) { super(rs); mID = id; @@ -131,12 +192,25 @@ public class Type extends BaseObj { public Type create() { Type t = internalCreate(mRS, this); t.mElement = mElement; - t.mDimensions = new Dimension[mEntryCount]; - t.mValues = new int[mEntryCount]; + for(int ct=0; ct < mEntryCount; ct++) { - t.mDimensions[ct] = mEntries[ct].mDim; - t.mValues[ct] = mEntries[ct].mValue; + if(mEntries[ct].mDim == Dimension.X) { + t.mDimX = mEntries[ct].mValue; + } + if(mEntries[ct].mDim == Dimension.Y) { + t.mDimY = mEntries[ct].mValue; + } + if(mEntries[ct].mDim == Dimension.Z) { + t.mDimZ = mEntries[ct].mValue; + } + if(mEntries[ct].mDim == Dimension.LOD) { + t.mDimLOD = mEntries[ct].mValue != 0; + } + if(mEntries[ct].mDim == Dimension.FACE) { + t.mDimFaces = mEntries[ct].mValue != 0; + } } + t.calcElementCount(); return t; } } diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index eae6f24..56a4223 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -181,7 +181,7 @@ nElementBegin(JNIEnv *_env, jobject _this) static void -nElementAdd(JNIEnv *_env, jobject _this, jint kind, jint type, jint norm, jint bits, jstring name) +nElementAdd(JNIEnv *_env, jobject _this, jint kind, jint type, jboolean norm, jint bits, jstring name) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); const char* n = NULL; @@ -255,28 +255,24 @@ static void * SF_LoadFloat(JNIEnv *_env, jobject _obj, jfieldID _field, void *bu static void * SF_SaveInt(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer) { - LOGE("Save Int"); _env->SetIntField(_obj, _field, ((int32_t *)buffer)[0]); return ((uint8_t *)buffer) + 4; } static void * SF_SaveShort(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer) { - LOGE("Save Short"); _env->SetShortField(_obj, _field, ((int16_t *)buffer)[0]); return ((uint8_t *)buffer) + 2; } static void * SF_SaveByte(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer) { - LOGE("Save Byte"); _env->SetByteField(_obj, _field, ((int8_t *)buffer)[0]); return ((uint8_t *)buffer) + 1; } static void * SF_SaveFloat(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer) { - LOGE("Save Float"); _env->SetFloatField(_obj, _field, ((float *)buffer)[0]); return ((uint8_t *)buffer) + 4; } @@ -363,14 +359,6 @@ nAllocationCreateTyped(JNIEnv *_env, jobject _this, jint e) return (jint) rsAllocationCreateTyped(con, (RsElement)e); } -static jint -nAllocationCreateSized(JNIEnv *_env, jobject _this, jint e, jint count) -{ - RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nAllocationCreateSized, con(%p), e(%p), count(%i)", con, (RsElement)e, count); - return (jint) rsAllocationCreateSized(con, (RsElement)e, count); -} - static void nAllocationUploadToTexture(JNIEnv *_env, jobject _this, jint a, jint mip) { @@ -480,36 +468,36 @@ nAllocationCreateFromBitmapBoxed(JNIEnv *_env, jobject _this, jint dstFmt, jbool static void -nAllocationData_i(JNIEnv *_env, jobject _this, jint alloc, jintArray data, int sizeBytes) +nAllocationSubData1D_i(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jintArray data, int sizeBytes) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); jint len = _env->GetArrayLength(data); - LOG_API("nAllocationData_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len); + LOG_API("nAllocation1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes); jint *ptr = _env->GetIntArrayElements(data, NULL); - rsAllocationData(con, (RsAllocation)alloc, ptr, sizeBytes); + rsAllocation1DSubData(con, (RsAllocation)alloc, offset, count, ptr, sizeBytes); _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT); } static void -nAllocationData_f(JNIEnv *_env, jobject _this, jint alloc, jfloatArray data, int sizeBytes) +nAllocationSubData1D_s(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jshortArray data, int sizeBytes) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); jint len = _env->GetArrayLength(data); - LOG_API("nAllocationData_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len); - jfloat *ptr = _env->GetFloatArrayElements(data, NULL); - rsAllocationData(con, (RsAllocation)alloc, ptr, sizeBytes); - _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT); + LOG_API("nAllocation1DSubData_s, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes); + jshort *ptr = _env->GetShortArrayElements(data, NULL); + rsAllocation1DSubData(con, (RsAllocation)alloc, offset, count, ptr, sizeBytes); + _env->ReleaseShortArrayElements(data, ptr, JNI_ABORT); } static void -nAllocationSubData1D_i(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jintArray data, int sizeBytes) +nAllocationSubData1D_b(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jbyteArray data, int sizeBytes) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); jint len = _env->GetArrayLength(data); - LOG_API("nAllocation1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAllocation)alloc, offset, count, len); - jint *ptr = _env->GetIntArrayElements(data, NULL); + LOG_API("nAllocation1DSubData_b, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes); + jbyte *ptr = _env->GetByteArrayElements(data, NULL); rsAllocation1DSubData(con, (RsAllocation)alloc, offset, count, ptr, sizeBytes); - _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT); + _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT); } static void @@ -517,7 +505,7 @@ nAllocationSubData1D_f(JNIEnv *_env, jobject _this, jint alloc, jint offset, jin { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); jint len = _env->GetArrayLength(data); - LOG_API("nAllocation1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAllocation)alloc, offset, count, len); + LOG_API("nAllocation1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes); jfloat *ptr = _env->GetFloatArrayElements(data, NULL); rsAllocation1DSubData(con, (RsAllocation)alloc, offset, count, ptr, sizeBytes); _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT); @@ -601,11 +589,8 @@ nAllocationSubReadFromObject(JNIEnv *_env, jobject _this, jint alloc, jobject _t void * buf = bufAlloc; rsAllocationRead(con, (RsAllocation)alloc, bufAlloc); - LOGE("size %i, ", tc->size); - for (int ct=0; ct < tc->fieldCount; ct++) { const TypeFieldCache *tfc = &tc->fields[ct]; - LOGE("ct=%i, buf=%p", ct, buf); buf = tfc->readPtr(_env, _o, tfc->field, buf); } free(bufAlloc); @@ -1330,7 +1315,7 @@ static JNINativeMethod methods[] = { {"nFileOpen", "([B)I", (void*)nFileOpen }, {"nElementBegin", "()V", (void*)nElementBegin }, -{"nElementAdd", "(IIIILjava/lang/String;)V", (void*)nElementAdd }, +{"nElementAdd", "(IIZILjava/lang/String;)V", (void*)nElementAdd }, {"nElementCreate", "()I", (void*)nElementCreate }, {"nTypeBegin", "(I)V", (void*)nTypeBegin }, @@ -1340,15 +1325,14 @@ static JNINativeMethod methods[] = { {"nTypeSetupFields", "(Landroid/renderscript/Type;[I[I[Ljava/lang/reflect/Field;)V", (void*)nTypeSetupFields }, {"nAllocationCreateTyped", "(I)I", (void*)nAllocationCreateTyped }, -{"nAllocationCreateSized", "(II)I", (void*)nAllocationCreateSized }, {"nAllocationCreateFromBitmap", "(IZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmap }, {"nAllocationCreateFromBitmapBoxed","(IZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmapBoxed }, {"nAllocationCreateFromAssetStream","(IZI)I", (void*)nAllocationCreateFromAssetStream }, {"nAllocationUploadToTexture", "(II)V", (void*)nAllocationUploadToTexture }, {"nAllocationUploadToBufferObject","(I)V", (void*)nAllocationUploadToBufferObject }, -{"nAllocationData", "(I[II)V", (void*)nAllocationData_i }, -{"nAllocationData", "(I[FI)V", (void*)nAllocationData_f }, {"nAllocationSubData1D", "(III[II)V", (void*)nAllocationSubData1D_i }, +{"nAllocationSubData1D", "(III[SI)V", (void*)nAllocationSubData1D_s }, +{"nAllocationSubData1D", "(III[BI)V", (void*)nAllocationSubData1D_b }, {"nAllocationSubData1D", "(III[FI)V", (void*)nAllocationSubData1D_f }, {"nAllocationSubData2D", "(IIIII[II)V", (void*)nAllocationSubData2D_i }, {"nAllocationSubData2D", "(IIIII[FI)V", (void*)nAllocationSubData2D_f }, diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index 28c2992..43042c0b 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -28,9 +28,12 @@ import java.security.KeyPair; */ public class Credentials { private static final String LOGTAG = "Credentials"; - private static final String UNLOCK_ACTION = "android.credentials.UNLOCK"; - private static final String INSTALL_ACTION = "android.credentials.INSTALL"; - private static Credentials singleton; + + public static final String UNLOCK_ACTION = "android.credentials.UNLOCK"; + + public static final String INSTALL_ACTION = "android.credentials.INSTALL"; + + public static final String SYSTEM_INSTALL_ACTION = "android.credentials.SYSTEM_INSTALL"; /** Key prefix for CA certificates. */ public static final String CA_CERTIFICATE = "CACERT_"; @@ -59,6 +62,8 @@ public class Credentials { /** Data type for PKCS12. */ public static final String PKCS12 = "PKCS12"; + private static Credentials singleton; + public static Credentials getInstance() { if (singleton == null) { singleton = new Credentials(); @@ -95,4 +100,12 @@ public class Credentials { Log.w(LOGTAG, e.toString()); } } + + public void installFromSdCard(Context context) { + try { + context.startActivity(new Intent(INSTALL_ACTION)); + } catch (ActivityNotFoundException e) { + Log.w(LOGTAG, e.toString()); + } + } } diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java new file mode 100644 index 0000000..a47534b --- /dev/null +++ b/keystore/java/android/security/KeyStore.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2009 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.security; + +import android.net.LocalSocketAddress; +import android.net.LocalSocket; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; + +/** + * {@hide} + */ +public class KeyStore { + public static int NO_ERROR = 1; + public static int LOCKED = 2; + public static int UNINITIALIZED = 3; + public static int SYSTEM_ERROR = 4; + public static int PROTOCOL_ERROR = 5; + public static int PERMISSION_DENIED = 6; + public static int KEY_NOT_FOUND = 7; + public static int VALUE_CORRUPTED = 8; + public static int UNDEFINED_ACTION = 9; + public static int WRONG_PASSWORD = 10; + + private static final LocalSocketAddress sAddress = new LocalSocketAddress( + "keystore", LocalSocketAddress.Namespace.RESERVED); + + private int mError = NO_ERROR; + + private KeyStore() {} + + public static KeyStore getInstance() { + return new KeyStore(); + } + + public int test() { + execute('t'); + return mError; + } + + public byte[] get(byte[] key) { + byte[][] values = execute('g', key); + return (values == null) ? null : values[0]; + } + + public String get(String key) { + byte[] value = get(key.getBytes()); + return (value == null) ? null : new String(value); + } + + public boolean put(byte[] key, byte[] value) { + execute('i', key, value); + return mError == NO_ERROR; + } + + public boolean put(String key, String value) { + return put(key.getBytes(), value.getBytes()); + } + + public boolean delete(byte[] key) { + execute('d', key); + return mError == NO_ERROR; + } + + public boolean delete(String key) { + return delete(key.getBytes()); + } + + public boolean contains(byte[] key) { + execute('e', key); + return mError == NO_ERROR; + } + + public boolean contains(String key) { + return contains(key.getBytes()); + } + + public byte[][] saw(byte[] prefix) { + return execute('s', prefix); + } + + public String[] saw(String prefix) { + byte[][] values = saw(prefix.getBytes()); + if (values == null) { + return null; + } + String[] strings = new String[values.length]; + for (int i = 0; i < values.length; ++i) { + strings[i] = new String(values[i]); + } + return strings; + } + + public boolean reset() { + execute('r'); + return mError == NO_ERROR; + } + + public boolean password(byte[] oldPassword, byte[] newPassword) { + execute('p', oldPassword, newPassword); + return mError == NO_ERROR; + } + + public boolean password(String oldPassword, String newPassword) { + return password(oldPassword.getBytes(), newPassword.getBytes()); + } + + public boolean password(byte[] password) { + return password(password, password); + } + + public boolean password(String password) { + return password(password.getBytes()); + } + + public boolean lock() { + execute('l'); + return mError == NO_ERROR; + } + + public boolean unlock(byte[] password) { + execute('u', password); + return mError == NO_ERROR; + } + + public boolean unlock(String password) { + return unlock(password.getBytes()); + } + + public int getLastError() { + return mError; + } + + private byte[][] execute(int code, byte[]... parameters) { + mError = PROTOCOL_ERROR; + + for (byte[] parameter : parameters) { + if (parameter == null || parameter.length > 65535) { + return null; + } + } + + LocalSocket socket = new LocalSocket(); + try { + socket.connect(sAddress); + + OutputStream out = socket.getOutputStream(); + out.write(code); + for (byte[] parameter : parameters) { + out.write(parameter.length >> 8); + out.write(parameter.length); + out.write(parameter); + } + out.flush(); + socket.shutdownOutput(); + + InputStream in = socket.getInputStream(); + code = in.read(); + if (code == -1) { + return null; + } + + ArrayList<byte[]> results = new ArrayList<byte[]>(); + while (true) { + int i, j; + if ((i = in.read()) == -1) { + break; + } + if ((j = in.read()) == -1) { + return null; + } + byte[] result = new byte[i << 8 | j]; + for (i = 0; i < result.length; i += j) { + if ((j = in.read(result, i, result.length - i)) == -1) { + return null; + } + } + results.add(result); + } + mError = code; + return results.toArray(new byte[results.size()][]); + } catch (IOException e) { + // ignore + } finally { + try { + socket.close(); + } catch (IOException e) {} + } + return null; + } +} diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index e534447..6500791 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -1227,44 +1227,46 @@ bool AudioFlinger::MixerThread::threadLoop() enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); } - - // output audio to hardware - if (mSuspended) { - usleep(kMaxBufferRecoveryInUsecs); + if (LIKELY(enabledTracks)) { + // mix buffers... + mAudioMixer->process(curBuf); + sleepTime = 0; + standbyTime = systemTime() + kStandbyTimeInNsecs; } else { - if (LIKELY(enabledTracks)) { - // mix buffers... - mAudioMixer->process(curBuf); + sleepTime += kBufferRecoveryInUsecs; + if (sleepTime > kMaxBufferRecoveryInUsecs) { + sleepTime = kMaxBufferRecoveryInUsecs; + } + // There was nothing to mix this round, which means all + // active tracks were late. Sleep a little bit to give + // them another chance. If we're too late, write 0s to audio + // hardware to avoid underrun. + if (mBytesWritten != 0 && sleepTime >= kMaxBufferRecoveryInUsecs) { + memset (curBuf, 0, mixBufferSize); sleepTime = 0; - standbyTime = systemTime() + kStandbyTimeInNsecs; - } else { - sleepTime += kBufferRecoveryInUsecs; - // There was nothing to mix this round, which means all - // active tracks were late. Sleep a little bit to give - // them another chance. If we're too late, write 0s to audio - // hardware to avoid underrun. - if (mBytesWritten == 0 || sleepTime < kMaxBufferRecoveryInUsecs) { - usleep(kBufferRecoveryInUsecs); - } else { - memset (curBuf, 0, mixBufferSize); - sleepTime = 0; - } } - // sleepTime == 0 means PCM data were written to mMixBuffer[] - if (sleepTime == 0) { - mLastWriteTime = systemTime(); - mInWrite = true; - int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize); - if (bytesWritten > 0) mBytesWritten += bytesWritten; - mNumWrites++; - mInWrite = false; - mStandby = false; - nsecs_t delta = systemTime() - mLastWriteTime; - if (delta > maxPeriod) { - LOGW("write blocked for %llu msecs", ns2ms(delta)); - mNumDelayedWrites++; - } + } + + if (mSuspended) { + sleepTime = kMaxBufferRecoveryInUsecs; + } + // sleepTime == 0 means we must write to audio hardware + if (sleepTime == 0) { + mLastWriteTime = systemTime(); + mInWrite = true; + LOGV("mOutput->write() thread %p frames %d", this, mFrameCount); + int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize); + if (bytesWritten > 0) mBytesWritten += bytesWritten; + mNumWrites++; + mInWrite = false; + mStandby = false; + nsecs_t delta = systemTime() - mLastWriteTime; + if (delta > maxPeriod) { + LOGW("write blocked for %llu msecs, thread %p", ns2ms(delta), this); + mNumDelayedWrites++; } + } else { + usleep(sleepTime); } // finally let go of all our tracks, without the lock held @@ -1718,50 +1720,55 @@ bool AudioFlinger::DirectOutputThread::threadLoop() } } - // output audio to hardware - if (mSuspended) { - usleep(kMaxBufferRecoveryInUsecs); - } else { - if (activeTrack != 0) { - AudioBufferProvider::Buffer buffer; - size_t frameCount = mFrameCount; - curBuf = (int8_t *)mMixBuffer; - // output audio to hardware - while(frameCount) { - buffer.frameCount = frameCount; - activeTrack->getNextBuffer(&buffer); - if (UNLIKELY(buffer.raw == 0)) { - memset(curBuf, 0, frameCount * mFrameSize); - break; - } - memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize); - frameCount -= buffer.frameCount; - curBuf += buffer.frameCount * mFrameSize; - activeTrack->releaseBuffer(&buffer); + if (activeTrack != 0) { + AudioBufferProvider::Buffer buffer; + size_t frameCount = mFrameCount; + curBuf = (int8_t *)mMixBuffer; + // output audio to hardware + while(frameCount) { + buffer.frameCount = frameCount; + activeTrack->getNextBuffer(&buffer); + if (UNLIKELY(buffer.raw == 0)) { + memset(curBuf, 0, frameCount * mFrameSize); + break; } + memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize); + frameCount -= buffer.frameCount; + curBuf += buffer.frameCount * mFrameSize; + activeTrack->releaseBuffer(&buffer); + } + sleepTime = 0; + standbyTime = systemTime() + kStandbyTimeInNsecs; + } else { + sleepTime += kBufferRecoveryInUsecs; + if (sleepTime > kMaxBufferRecoveryInUsecs) { + sleepTime = kMaxBufferRecoveryInUsecs; + } + // There was nothing to mix this round, which means all + // active tracks were late. Sleep a little bit to give + // them another chance. If we're too late, write 0s to audio + // hardware to avoid underrun. + if (mBytesWritten != 0 && sleepTime >= kMaxBufferRecoveryInUsecs && + AudioSystem::isLinearPCM(mFormat)) { + memset (mMixBuffer, 0, mFrameCount * mFrameSize); sleepTime = 0; - standbyTime = systemTime() + kStandbyTimeInNsecs; - } else { - sleepTime += kBufferRecoveryInUsecs; - if (mBytesWritten == 0 || !AudioSystem::isLinearPCM(mFormat) || - sleepTime < kMaxBufferRecoveryInUsecs) { - usleep(kBufferRecoveryInUsecs); - } else { - memset (mMixBuffer, 0, mFrameCount * mFrameSize); - sleepTime = 0; - } } + } - // sleepTime == 0 means PCM data were written to mMixBuffer[] - if (sleepTime == 0) { - mLastWriteTime = systemTime(); - mInWrite = true; - int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize); - if (bytesWritten) mBytesWritten += bytesWritten; - mNumWrites++; - mInWrite = false; - mStandby = false; - } + if (mSuspended) { + sleepTime = kMaxBufferRecoveryInUsecs; + } + // sleepTime == 0 means we must write to audio hardware + if (sleepTime == 0) { + mLastWriteTime = systemTime(); + mInWrite = true; + int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize); + if (bytesWritten) mBytesWritten += bytesWritten; + mNumWrites++; + mInWrite = false; + mStandby = false; + } else { + usleep(sleepTime); } // finally let go of removed track, without the lock held @@ -1913,38 +1920,40 @@ bool AudioFlinger::DuplicatingThread::threadLoop() } enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); - } + } - bool mustSleep = true; if (LIKELY(enabledTracks)) { // mix buffers... mAudioMixer->process(curBuf); - if (!mSuspended) { - for (size_t i = 0; i < outputTracks.size(); i++) { - outputTracks[i]->write(curBuf, mFrameCount); - mustSleep = false; - } - mStandby = false; - mBytesWritten += mixBufferSize; - } + sleepTime = 0; + standbyTime = systemTime() + kStandbyTimeInNsecs; } else { - // flush remaining overflow buffers in output tracks - for (size_t i = 0; i < outputTracks.size(); i++) { - if (outputTracks[i]->isActive()) { - outputTracks[i]->write(curBuf, 0); - standbyTime = systemTime() + kStandbyTimeInNsecs; - mustSleep = false; - } + sleepTime += kBufferRecoveryInUsecs; + if (sleepTime > kMaxBufferRecoveryInUsecs) { + sleepTime = kMaxBufferRecoveryInUsecs; + } + // There was nothing to mix this round, which means all + // active tracks were late. Sleep a little bit to give + // them another chance. If we're too late, write 0s to audio + // hardware to avoid underrun. + if (mBytesWritten != 0 && sleepTime >= kMaxBufferRecoveryInUsecs) { + memset (curBuf, 0, mixBufferSize); + sleepTime = 0; } } - if (mustSleep) { -// LOGV("threadLoop() sleeping %d", sleepTime); - usleep(sleepTime); - if (sleepTime < kMaxBufferRecoveryInUsecs) { - sleepTime += kBufferRecoveryInUsecs; + + if (mSuspended) { + sleepTime = kMaxBufferRecoveryInUsecs; + } + // sleepTime == 0 means we must write to audio hardware + if (sleepTime == 0) { + for (size_t i = 0; i < outputTracks.size(); i++) { + outputTracks[i]->write(curBuf, mFrameCount); } + mStandby = false; + mBytesWritten += mixBufferSize; } else { - sleepTime = kBufferRecoveryInUsecs; + usleep(sleepTime); } // finally let go of all our tracks, without the lock held diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 04f6e07..3e4c9af 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -112,8 +112,8 @@ bool Context::runRootScript() #endif rsAssert(mRootScript->mEnviroment.mIsRoot); - //glColor4f(1,1,1,1); - //glEnable(GL_LIGHT0); + eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_WIDTH, &mEGL.mWidth); + eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_HEIGHT, &mEGL.mHeight); glViewport(0, 0, mEGL.mWidth, mEGL.mHeight); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glEnable(GL_POINT_SMOOTH); diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp index d10076b..6dc3e8b 100644 --- a/libs/rs/rsScriptC_Lib.cpp +++ b/libs/rs/rsScriptC_Lib.cpp @@ -321,6 +321,16 @@ static int SC_sqr(int v) return v * v; } +static float SC_fracf(float v) +{ + return v - floorf(v); +} + +static float SC_roundf(float v) +{ + return floorf(v + 0.4999999999); +} + static float SC_distf2(float x1, float y1, float x2, float y2) { float x = x2 - x1; @@ -1014,8 +1024,12 @@ ScriptCState::SymbolTable_t ScriptCState::gSyms[] = { "float", "(float, float)" }, { "floorf", (void *)&floorf, "float", "(float)" }, + { "fracf", (void *)&SC_fracf, + "float", "(float)" }, { "ceilf", (void *)&ceilf, "float", "(float)" }, + { "roundf", (void *)&SC_roundf, + "float", "(float)" }, { "expf", (void *)&expf, "float", "(float)" }, { "logf", (void *)&logf, diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index 8dfc2cf..5ff9284 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -88,7 +88,6 @@ void Layer::destroy() mBuffers[i].clear(); mWidth = mHeight = 0; } - mSurface.clear(); } sp<LayerBaseClient::Surface> Layer::createSurface() const @@ -99,7 +98,8 @@ sp<LayerBaseClient::Surface> Layer::createSurface() const status_t Layer::ditch() { // the layer is not on screen anymore. free as much resources as possible - destroy(); + //destroy(); + mSurface.clear(); return NO_ERROR; } diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index e87b563..831c446 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -1084,9 +1084,12 @@ status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase) { - // remove the layer from the main list (through a transaction). + // First add the layer to the purgatory list, which makes sure it won't + // go away, then remove it from the main list (through a transaction). ssize_t err = removeLayer_l(layerBase); - + if (err >= 0) { + mLayerPurgatory.add(layerBase); + } // it's possible that we don't find a layer, because it might // have been destroyed already -- this is not technically an error // from the user because there is a race between BClient::destroySurface(), @@ -1359,8 +1362,18 @@ status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer) * to use the purgatory. */ status_t err = flinger->removeLayer_l(l); - LOGE_IF(err<0 && err != NAME_NOT_FOUND, - "error removing layer=%p (%s)", l.get(), strerror(-err)); + if (err == NAME_NOT_FOUND) { + // The surface wasn't in the current list, which means it was + // removed already, which means it is in the purgatory, + // and need to be removed from there. + // This needs to happen from the main thread since its dtor + // must run from there (b/c of OpenGL ES). Additionally, we + // can't really acquire our internal lock from + // destroySurface() -- see postMessage() below. + ssize_t idx = flinger->mLayerPurgatory.remove(l); + LOGE_IF(idx < 0, + "layer=%p is not in the purgatory list", l.get()); + } return true; } }; diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index f207f85..493e777 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -250,7 +250,9 @@ private: GraphicPlane& graphicPlane(int dpy); void waitForEvent(); +public: // hack to work around gcc 4.0.3 bug void signalEvent(); +private: void signalDelayedEvent(nsecs_t delay); void handleConsoleEvents(); @@ -312,6 +314,7 @@ private: volatile int32_t mTransactionCount; Condition mTransactionCV; bool mResizeTransationPending; + SortedVector< sp<LayerBase> > mLayerPurgatory; // protected by mStateLock (but we could use another lock) Tokenizer mTokens; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index f80843d..a5a8cc9 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1740,7 +1740,11 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const const int e = Res_GETENTRY(resID); if (p < 0) { - LOGW("No package identifier when getting name for resource number 0x%08x", resID); + if (Res_GETPACKAGE(resID)+1 == 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + } else { + LOGW("Resources don't contain pacakge for resource number 0x%08x", resID); + } return false; } if (t < 0) { @@ -1786,7 +1790,11 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag const int e = Res_GETENTRY(resID); if (p < 0) { - LOGW("No package identifier when getting value for resource number 0x%08x", resID); + if (Res_GETPACKAGE(resID)+1 == 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + } else { + LOGW("Resources don't contain pacakge for resource number 0x%08x", resID); + } return BAD_INDEX; } if (t < 0) { diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 37a3bd5..3ac5df5 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -736,6 +736,28 @@ public class MediaScanner if (time != -1) { values.put(Images.Media.DATE_TAKEN, time); } + + int orientation = exif.getAttributeInt( + ExifInterface.TAG_ORIENTATION, -1); + if (orientation != -1) { + // We only recognize a subset of orientation tag values. + int degree; + switch(orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: + degree = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + degree = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + degree = 270; + break; + default: + degree = 0; + break; + } + values.put(Images.Media.ORIENTATION, degree); + } } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java index 0401390..53afb1d 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java @@ -32,6 +32,29 @@ public class MediaProfileReader { return s; } + public static boolean getWMAEnable() { + // push all the property into one big table + int wmaEnable = 1; + wmaEnable = SystemProperties.getInt("ro.media.dec.aud.wma.enabled", + wmaEnable); + if (wmaEnable == 1) { + return true; + } else { + return false; + } + } + + public static boolean getWMVEnable(){ + int wmvEnable = 1; + wmvEnable = SystemProperties.getInt("ro.media.dec.vid.wmv.enabled", + wmvEnable); + if (wmvEnable == 1) { + return true; + } else { + return false; + } + } + public static void createVideoProfileTable() { // push all the property into one big table String encoderType = getVideoCodecProperty(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java index 30e2d6c..392d1d5 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java @@ -18,6 +18,7 @@ package com.android.mediaframeworktest.functional; import com.android.mediaframeworktest.MediaFrameworkTest; import com.android.mediaframeworktest.MediaNames; +import com.android.mediaframeworktest.MediaProfileReader; import android.content.Context; import android.test.ActivityInstrumentationTestCase; @@ -35,11 +36,15 @@ import java.io.File; public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFrameworkTest> { private boolean duratoinWithinTolerence = false; private String TAG = "MediaPlayerApiTest"; + private boolean isWMAEnable = false; + private boolean isWMVEnable = false; Context mContext; public MediaPlayerApiTest() { super("com.android.mediaframeworktest", MediaFrameworkTest.class); + isWMAEnable = MediaProfileReader.getWMAEnable(); + isWMVEnable = MediaProfileReader.getWMVEnable(); } protected void setUp() throws Exception { @@ -82,9 +87,11 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @MediumTest public void testWMA9GetDuration() throws Exception { - int duration = CodecTest.getDuration(MediaNames.WMA9); - duratoinWithinTolerence = verifyDuration(duration, MediaNames.WMA9_LENGTH); - assertTrue("WMA9 getDuration", duratoinWithinTolerence); + if (isWMAEnable) { + int duration = CodecTest.getDuration(MediaNames.WMA9); + duratoinWithinTolerence = verifyDuration(duration, MediaNames.WMA9_LENGTH); + assertTrue("WMA9 getDuration", duratoinWithinTolerence); + } } @MediumTest @@ -123,8 +130,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testWMA9GetCurrentPosition() throws Exception { - boolean currentPosition = CodecTest.getCurrentPosition(MediaNames.WMA9); - assertTrue("WMA9 GetCurrentPosition", currentPosition); + if (isWMAEnable) { + boolean currentPosition = CodecTest.getCurrentPosition(MediaNames.WMA9); + assertTrue("WMA9 GetCurrentPosition", currentPosition); + } } @LargeTest @@ -160,8 +169,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testWMA9Pause() throws Exception { - boolean isPaused = CodecTest.pause(MediaNames.WMA9); - assertTrue("WMA9 Pause", isPaused); + if (isWMAEnable) { + boolean isPaused = CodecTest.pause(MediaNames.WMA9); + assertTrue("WMA9 Pause", isPaused); + } } @LargeTest @@ -269,8 +280,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testWMA9SeekTo() throws Exception { - boolean isLoop = CodecTest.seekTo(MediaNames.WMA9); - assertTrue("WMA9 seekTo", isLoop); + if (isWMAEnable) { + boolean isLoop = CodecTest.seekTo(MediaNames.WMA9); + assertTrue("WMA9 seekTo", isLoop); + } } @LargeTest @@ -309,8 +322,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @Suppress @LargeTest public void testWMA9SeekToEnd() throws Exception { - boolean isEnd = CodecTest.seekToEnd(MediaNames.WMA9); - assertTrue("WMA9 seekToEnd", isEnd); + if (isWMAEnable) { + boolean isEnd = CodecTest.seekToEnd(MediaNames.WMA9); + assertTrue("WMA9 seekToEnd", isEnd); + } } @LargeTest @@ -327,8 +342,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testWAVSeekToEnd() throws Exception { - boolean isEnd = CodecTest.seekToEnd(MediaNames.WAV); - assertTrue("WAV seekToEnd", isEnd); + if (isWMVEnable) { + boolean isEnd = CodecTest.seekToEnd(MediaNames.WAV); + assertTrue("WAV seekToEnd", isEnd); + } } @MediumTest @@ -385,8 +402,12 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testVideoWMVSeekTo() throws Exception { - boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_WMV); - assertTrue("WMV SeekTo", isSeek); + Log.v(TAG, "wmv not enable"); + if (isWMVEnable) { + Log.v(TAG, "wmv enable"); + boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_WMV); + assertTrue("WMV SeekTo", isSeek); + } } @LargeTest diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java index 4a4ad6f..690eff6 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java @@ -214,6 +214,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram Log.v(TAG, "before getduration"); mOutputDuration = mediaPlayer.getDuration(); Log.v(TAG, "get video dimension"); + Thread.sleep(1000); mOutputVideoHeight = mediaPlayer.getVideoHeight(); mOutputVideoWidth = mediaPlayer.getVideoWidth(); //mOutputVideoHeight = CodecTest.videoHeight(outputFilePath); @@ -238,7 +239,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram validVideo = true; } Log.v(TAG, "width = " + mOutputVideoWidth + " height = " + mOutputVideoHeight + " Duration = " + mOutputDuration); - removeFile(filePath); + //removeFile(filePath); return validVideo; } @@ -416,7 +417,8 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram MediaRecorder.OutputFormat.THREE_GPP, MediaNames.RECORDED_VIDEO_3GP, false); assertTrue("Invalid video Size", isTestInvalidVideoSizeSuccessful); } - + + @Suppress @LargeTest public void testInvalidFrameRate() throws Exception { boolean isTestInvalidFrameRateSuccessful = false; @@ -426,8 +428,9 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram } @LargeTest - //est cases for the new codec + //test cases for the new codec public void testDeviceSpecificCodec() throws Exception { + int noOfFailure = 0; boolean recordSuccess = false; String deviceType = MediaProfileReader.getDeviceType(); Log.v(TAG, "deviceType = " + deviceType); @@ -448,10 +451,18 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram } else { recordSuccess = recordVideoWithPara(encoder[i], audio[j], "low"); } - assertTrue((encoder[i] + audio[j]), recordSuccess); + if (!recordSuccess){ + Log.v(TAG, "testDeviceSpecificCodec failed"); + Log.v(TAG, "Encoder = " + encoder[i] + "Audio Encoder = " + audio[j]); + noOfFailure++; + } + //assertTrue((encoder[i] + audio[j]), recordSuccess); } } } } + if (noOfFailure != 0){ + assertTrue("testDeviceSpecificCodec", false); + } } } diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java index 8b457f6..b89b707 100644 --- a/obex/javax/obex/HeaderSet.java +++ b/obex/javax/obex/HeaderSet.java @@ -35,7 +35,7 @@ package javax.obex; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Calendar; -import java.util.Random; +import java.security.SecureRandom; /** * This class implements the javax.obex.HeaderSet interface for OBEX over @@ -209,7 +209,7 @@ public final class HeaderSet { private Long[] mIntegerUserDefined; // 4 byte unsigned integer - private final Random mRandom; + private final SecureRandom mRandom; /*package*/ byte[] nonce; @@ -231,7 +231,7 @@ public final class HeaderSet { mByteUserDefined = new Byte[16]; mIntegerUserDefined = new Long[16]; responseCode = -1; - mRandom = new Random(); + mRandom = new SecureRandom(); } /** diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 022da0c..b3a19e3 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -271,18 +271,52 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * @param renderer the renderer to use to perform OpenGL drawing. */ public void setRenderer(Renderer renderer) { - if (mGLThread != null) { - throw new IllegalStateException( - "setRenderer has already been called for this instance."); - } + checkRenderThreadState(); if (mEGLConfigChooser == null) { mEGLConfigChooser = new SimpleEGLConfigChooser(true); } + if (mEGLContextFactory == null) { + mEGLContextFactory = new DefaultContextFactory(); + } + if (mEGLWindowSurfaceFactory == null) { + mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); + } mGLThread = new GLThread(renderer); mGLThread.start(); } /** + * @hide + * Install a custom EGLContextFactory. + * <p>If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + * <p> + * If this method is not called, then by default + * a context will be created with no shared context and + * with a null attribute list. + */ + public void setContextFactory(EGLContextFactory factory) { + checkRenderThreadState(); + mEGLContextFactory = factory; + } + + /** + * @hide + * Install a custom EGLWindowSurfaceFactory. + * <p>If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + * <p> + * If this method is not called, then by default + * a window surface will be created with a null attribute list. + */ + public void setWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { + checkRenderThreadState(); + mEGLWindowSurfaceFactory = factory; + } + + /** * Install a custom EGLConfigChooser. * <p>If this method is * called, it must be called before {@link #setRenderer(Renderer)} @@ -294,10 +328,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * @param configChooser */ public void setEGLConfigChooser(EGLConfigChooser configChooser) { - if (mGLThread != null) { - throw new IllegalStateException( - "setRenderer has already been called for this instance."); - } + checkRenderThreadState(); mEGLConfigChooser = configChooser; } @@ -578,6 +609,56 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** + * @hide + * An interface for customizing the eglCreateContext and eglDestroyContext calls. + * <p> + * This interface must be implemented by clients wishing to call + * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)} + */ + public interface EGLContextFactory { + EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); + void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); + } + + private static class DefaultContextFactory implements EGLContextFactory { + + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { + return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, null); + } + + public void destroyContext(EGL10 egl, EGLDisplay display, + EGLContext context) { + egl.eglDestroyContext(display, context); + } + } + + /** + * @hide + * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. + * <p> + * This interface must be implemented by clients wishing to call + * {@link GLSurfaceView#setEGLContextCreator(EGLContextCreator)} + */ + public interface EGLWindowSurfaceFactory { + EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, + Object nativeWindow); + void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface); + } + + private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { + + public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, + EGLConfig config, Object nativeWindow) { + return egl.eglCreateWindowSurface(display, config, nativeWindow, null); + } + + public void destroySurface(EGL10 egl, EGLDisplay display, + EGLSurface surface) { + egl.eglDestroySurface(display, surface); + } + } + + /** * An interface for choosing an EGLConfig configuration from a list of * potential configurations. * <p> @@ -751,8 +832,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * Create an OpenGL ES context. This must be done only once, an * OpenGL context is a somewhat heavy object. */ - mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, - EGL10.EGL_NO_CONTEXT, null); + mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); mEglSurface = null; } @@ -774,14 +854,14 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback */ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - mEgl.eglDestroySurface(mEglDisplay, mEglSurface); + mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); } /* * Create an EGL surface we can render into. */ - mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, - mEglConfig, holder, null); + mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl, + mEglDisplay, mEglConfig, holder); /* * Before we can issue GL commands, we need to make sure @@ -790,7 +870,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); - GL gl = mEglContext.getGL(); if (mGLWrapper != null) { gl = mGLWrapper.wrap(gl); @@ -826,16 +905,19 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST; } - public void finish() { + public void destroySurface() { if (mEglSurface != null) { mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - mEgl.eglDestroySurface(mEglDisplay, mEglSurface); + mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); mEglSurface = null; } + } + + public void finish() { if (mEglContext != null) { - mEgl.eglDestroyContext(mEglDisplay, mEglContext); + mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext); mEglContext = null; } if (mEglDisplay != null) { @@ -895,78 +977,93 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private void guardedRun() throws InterruptedException { mEglHelper = new EglHelper(); - mEglHelper.start(); + try { + mEglHelper.start(); - GL10 gl = null; - boolean tellRendererSurfaceCreated = true; - boolean tellRendererSurfaceChanged = true; - - /* - * This is our main activity thread's loop, we go until - * asked to quit. - */ - while (!mDone) { + GL10 gl = null; + boolean tellRendererSurfaceCreated = true; + boolean tellRendererSurfaceChanged = true; /* - * Update the asynchronous state (window size) + * This is our main activity thread's loop, we go until + * asked to quit. */ - int w, h; - boolean changed; - boolean needStart = false; - synchronized (this) { - Runnable r; - while ((r = getEvent()) != null) { - r.run(); + while (!mDone) { + + /* + * Update the asynchronous state (window size) + */ + int w, h; + boolean changed; + boolean needStart = false; + synchronized (this) { + Runnable r; + while ((r = getEvent()) != null) { + r.run(); + } + if (mPaused) { + mEglHelper.destroySurface(); + mEglHelper.finish(); + needStart = true; + } + while (needToWait()) { + if (!mHasSurface) { + if (!mWaitingForSurface) { + mEglHelper.destroySurface(); + mWaitingForSurface = true; + notify(); + } + } + wait(); + } + if (mDone) { + break; + } + changed = mSizeChanged; + w = mWidth; + h = mHeight; + mSizeChanged = false; + mRequestRender = false; + if (mHasSurface && mWaitingForSurface) { + changed = true; + mWaitingForSurface = false; + } } - if (mPaused) { - mEglHelper.finish(); - needStart = true; + if (needStart) { + mEglHelper.start(); + tellRendererSurfaceCreated = true; + changed = true; } - while (needToWait()) { - wait(); + if (changed) { + gl = (GL10) mEglHelper.createSurface(getHolder()); + tellRendererSurfaceChanged = true; } - if (mDone) { - break; + if (tellRendererSurfaceCreated) { + mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); + tellRendererSurfaceCreated = false; } - changed = mSizeChanged; - w = mWidth; - h = mHeight; - mSizeChanged = false; - mRequestRender = false; - } - if (needStart) { - mEglHelper.start(); - tellRendererSurfaceCreated = true; - changed = true; - } - if (changed) { - gl = (GL10) mEglHelper.createSurface(getHolder()); - tellRendererSurfaceChanged = true; - } - if (tellRendererSurfaceCreated) { - mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); - tellRendererSurfaceCreated = false; - } - if (tellRendererSurfaceChanged) { - mRenderer.onSurfaceChanged(gl, w, h); - tellRendererSurfaceChanged = false; - } - if ((w > 0) && (h > 0)) { - /* draw a frame here */ - mRenderer.onDrawFrame(gl); - - /* - * Once we're done with GL, we need to call swapBuffers() - * to instruct the system to display the rendered frame - */ - mEglHelper.swap(); - } - } - - /* - * clean-up everything... - */ - mEglHelper.finish(); + if (tellRendererSurfaceChanged) { + mRenderer.onSurfaceChanged(gl, w, h); + tellRendererSurfaceChanged = false; + } + if ((w > 0) && (h > 0)) { + /* draw a frame here */ + mRenderer.onDrawFrame(gl); + + /* + * Once we're done with GL, we need to call swapBuffers() + * to instruct the system to display the rendered frame + */ + mEglHelper.swap(); + } + } + } finally { + /* + * clean-up everything... + */ + mEglHelper.destroySurface(); + mEglHelper.finish(); + } } private boolean needToWait() { @@ -1021,6 +1118,13 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback synchronized(this) { mHasSurface = false; notify(); + while(!mWaitingForSurface && isAlive()) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } } } @@ -1083,6 +1187,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private boolean mDone; private boolean mPaused; private boolean mHasSurface; + private boolean mWaitingForSurface; private int mWidth; private int mHeight; private int mRenderMode; @@ -1124,11 +1229,21 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private StringBuilder mBuilder = new StringBuilder(); } + + private void checkRenderThreadState() { + if (mGLThread != null) { + throw new IllegalStateException( + "setRenderer has already been called for this instance."); + } + } + private static final Semaphore sEglSemaphore = new Semaphore(1); private boolean mSizeChanged = true; private GLThread mGLThread; private EGLConfigChooser mEGLConfigChooser; + private EGLContextFactory mEGLContextFactory; + private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory; private GLWrapper mGLWrapper; private int mDebugFlags; } diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index d51b333..7e7da1b 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -222,19 +222,28 @@ void Loader::init_api(void* dso, } } -void *Loader::load_driver(const char* driver, gl_hooks_t* hooks, uint32_t mask) +void *Loader::load_driver(const char* driver_absolute_path, + gl_hooks_t* hooks, uint32_t mask) { - void* dso = dlopen(driver, RTLD_NOW | RTLD_LOCAL); - if (dso == 0) + if (access(driver_absolute_path, R_OK)) { + // this happens often, we don't want to log an error return 0; + } + + void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL); + if (dso == 0) { + const char* err = dlerror(); + LOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown"); + return 0; + } - LOGD("loaded %s", driver); + LOGD("loaded %s", driver_absolute_path); if (mask & EGL) { getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress"); LOGE_IF(!getProcAddress, - "can't find eglGetProcAddress() in %s", driver); + "can't find eglGetProcAddress() in %s", driver_absolute_path); gl_hooks_t::egl_t* egl = &hooks->egl; __eglMustCastToProperFunctionPointerType* curr = diff --git a/opengl/tests/gl2_basic/gl2_basic.cpp b/opengl/tests/gl2_basic/gl2_basic.cpp index d4887ba..f97d347 100644 --- a/opengl/tests/gl2_basic/gl2_basic.cpp +++ b/opengl/tests/gl2_basic/gl2_basic.cpp @@ -21,8 +21,8 @@ #include <sys/resource.h> #include <EGL/egl.h> -#include <GLES/gl.h> -#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> #include <utils/Timers.h> @@ -31,144 +31,267 @@ using namespace android; -static void printGLString(const char *name, GLenum s) -{ - fprintf(stderr, "printGLString %s, %d\n", name, s); -#if 0 // causes hangs - const char *v = (const char *)glGetString(s); - int error = glGetError(); - fprintf(stderr, "glGetError() = %d, result of glGetString = %x\n", error, - (unsigned int)v); - if ((v < (const char*) 0) || (v > (const char*) 0x10000)) - fprintf(stderr, "GL %s = %s\n", name, v); - else - fprintf(stderr, "GL %s = (null) 0x%08x\n", name, (unsigned int) v); -#endif +static void printGLString(const char *name, GLenum s) { + // fprintf(stderr, "printGLString %s, %d\n", name, s); + const char *v = (const char *) glGetString(s); + // int error = glGetError(); + // fprintf(stderr, "glGetError() = %d, result of glGetString = %x\n", error, + // (unsigned int) v); + // if ((v < (const char*) 0) || (v > (const char*) 0x10000)) + // fprintf(stderr, "GL %s = %s\n", name, v); + // else + // fprintf(stderr, "GL %s = (null) 0x%08x\n", name, (unsigned int) v); + fprintf(stderr, "GL %s = %s\n", name, v); } static const char* eglErrorToString[] = { - "EGL_SUCCESS", // 0x3000 12288 - "EGL_NOT_INITIALIZED", - "EGL_BAD_ACCESS", // 0x3002 12290 - "EGL_BAD_ALLOC", - "EGL_BAD_ATTRIBUTE", - "EGL_BAD_CONFIG", - "EGL_BAD_CONTEXT", // 0x3006 12294 - "EGL_BAD_CURRENT_SURFACE", - "EGL_BAD_DISPLAY", - "EGL_BAD_MATCH", - "EGL_BAD_NATIVE_PIXMAP", - "EGL_BAD_NATIVE_WINDOW", - "EGL_BAD_PARAMETER", // 0x300c 12300 - "EGL_BAD_SURFACE" -}; + "EGL_SUCCESS", // 0x3000 12288 + "EGL_NOT_INITIALIZED", + "EGL_BAD_ACCESS", // 0x3002 12290 + "EGL_BAD_ALLOC", "EGL_BAD_ATTRIBUTE", + "EGL_BAD_CONFIG", + "EGL_BAD_CONTEXT", // 0x3006 12294 + "EGL_BAD_CURRENT_SURFACE", "EGL_BAD_DISPLAY", "EGL_BAD_MATCH", + "EGL_BAD_NATIVE_PIXMAP", "EGL_BAD_NATIVE_WINDOW", "EGL_BAD_PARAMETER", // 0x300c 12300 + "EGL_BAD_SURFACE" }; static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) { if (returnVal != EGL_TRUE) { fprintf(stderr, "%s() returned %d\n", op, returnVal); } - for(EGLint error = eglGetError(); - error != EGL_SUCCESS; - error = eglGetError()) { + for (EGLint error = eglGetError(); error != EGL_SUCCESS; error + = eglGetError()) { const char* errorString = "unknown"; if (error >= EGL_SUCCESS && error <= EGL_BAD_SURFACE) { errorString = eglErrorToString[error - EGL_SUCCESS]; } - fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, - errorString, error); + fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, errorString, + error); + } +} + +static void checkGlError(const char* op) { + for (GLint error = glGetError(); error; error + = glGetError()) { + fprintf(stderr, "after %s() glError (0x%x)\n", op, error); + } +} + +static const char gVertexShader[] = "attribute vec4 vPosition;\n" + "void main() {\n" + " gl_Position = vPosition;\n" + "}\n"; + +static const char gFragmentShader[] = "precision mediump float;\n" + "void main() {\n" + " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" + "}\n"; + +GLuint loadShader(GLenum shaderType, const char* pSource) { + GLuint shader = glCreateShader(shaderType); + if (shader) { + glShaderSource(shader, 1, &pSource, NULL); + glCompileShader(shader); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen) { + char* buf = (char*) malloc(infoLen); + if (buf) { + glGetShaderInfoLog(shader, infoLen, NULL, buf); + fprintf(stderr, "Could not compile shader %d:\n%s\n", + shaderType, buf); + free(buf); + } + glDeleteShader(shader); + shader = 0; + } + } + } + return shader; +} + +GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) { + GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); + if (!vertexShader) { + return 0; + } + + GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); + if (!pixelShader) { + return 0; + } + + GLuint program = glCreateProgram(); + if (program) { + glAttachShader(program, vertexShader); + checkGlError("glAttachShader"); + glAttachShader(program, pixelShader); + checkGlError("glAttachShader"); + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if (linkStatus != GL_TRUE) { + GLint bufLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); + if (bufLength) { + char* buf = (char*) malloc(bufLength); + if (buf) { + glGetProgramInfoLog(program, bufLength, NULL, buf); + fprintf(stderr, "Could not link program:\n%s\n", buf); + free(buf); + } + } + glDeleteProgram(program); + program = 0; + } + } + return program; +} + +GLuint gProgram; +GLuint gvPositionHandle; + +bool setupGraphics(int w, int h) { + gProgram = createProgram(gVertexShader, gFragmentShader); + if (!gProgram) { + return false; } + gvPositionHandle = glGetAttribLocation(gProgram, "vPosition"); + checkGlError("glGetAttribLocation"); + fprintf(stderr, "glGetAttribLocation(\"vPosition\") = %d\n", + gvPositionHandle); + + glViewport(0, 0, w, h); + checkGlError("glViewport"); + return true; +} + +const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f }; + +void renderFrame() { + glClearColor(0.0f, 0.0f, 1.0f, 1.0f); + checkGlError("glClearColor"); + glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + checkGlError("glClear"); + + glUseProgram(gProgram); + checkGlError("glUseProgram"); + + glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices); + checkGlError("glVertexAttribPointer"); + glEnableVertexAttribArray(gvPositionHandle); + checkGlError("glEnableVertexAttribArray"); + glDrawArrays(GL_TRIANGLES, 0, 3); + checkGlError("glDrawArrays"); } -int main(int argc, char** argv) -{ +int main(int argc, char** argv) { EGLBoolean returnValue; EGLConfig configs[2]; EGLint config_count; - EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - EGLint s_configAttribs[] = { - EGL_BUFFER_SIZE, EGL_DONT_CARE, - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, - EGL_DEPTH_SIZE, 8, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - EGLint majorVersion; - EGLint minorVersion; - EGLContext context; - EGLSurface surface; - EGLint w, h; - - EGLDisplay dpy; - - EGLNativeWindowType window = 0; - window = android_createDisplaySurface(); - - checkEglError("<init>"); - dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - checkEglError("eglGetDisplay"); - if (dpy == EGL_NO_DISPLAY) { - printf("eglGetDisplay returned EGL_NO_DISPLAY.\n"); - return 0; - } - returnValue = eglInitialize(dpy, &majorVersion, &minorVersion); - checkEglError("eglInitialize", returnValue); - fprintf(stderr, "EGL version %d.%d\n", majorVersion, minorVersion); - - returnValue = eglGetConfigs (dpy, configs, 2, &config_count); - checkEglError("eglGetConfigs", returnValue); - fprintf(stderr, "Config count: %d\n", config_count); - for(int i = 0; i < config_count; i++) { + EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + EGLint s_configAttribs[] = { EGL_BUFFER_SIZE, EGL_DONT_CARE, EGL_RED_SIZE, + 5, EGL_GREEN_SIZE, 6, EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; + + EGLint s_configAttribs2[] = + { + EGL_DEPTH_SIZE, 16, + EGL_NONE + }; + + EGLint majorVersion; + EGLint minorVersion; + EGLContext context; + EGLSurface surface; + EGLint w, h; + + EGLDisplay dpy; + + EGLNativeWindowType window = 0; + window = android_createDisplaySurface(); + + checkEglError("<init>"); + dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + checkEglError("eglGetDisplay"); + if (dpy == EGL_NO_DISPLAY) { + printf("eglGetDisplay returned EGL_NO_DISPLAY.\n"); + return 0; + } + + returnValue = eglInitialize(dpy, &majorVersion, &minorVersion); + checkEglError("eglInitialize", returnValue); + fprintf(stderr, "EGL version %d.%d\n", majorVersion, minorVersion); + if (returnValue != EGL_TRUE) { + printf("eglInitialize failed\n"); + return 0; + } + + returnValue = eglGetConfigs(dpy, configs, 2, &config_count); + checkEglError("eglGetConfigs", returnValue); + fprintf(stderr, "Config count: %d\n", config_count); + for (int i = 0; i < config_count; i++) { fprintf(stderr, "%d: 0x%08x\n", i, (unsigned int) configs[i]); - } + } + #if 0 - EGLConfig config; - EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &config); - checkEglError("EGLUtils::selectConfigForNativeWindow"); + EGLConfig config; + EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &config); + checkEglError("EGLUtils::selectConfigForNativeWindow"); #else - int chooseConfigResult = eglChooseConfig(dpy, s_configAttribs, configs, 2, &config_count); + int chooseConfigResult = eglChooseConfig(dpy, s_configAttribs2, configs, 2, + &config_count); checkEglError("eglChooseConfig", chooseConfigResult); - if (chooseConfigResult != EGL_TRUE )
- {
+ if (chooseConfigResult != EGL_TRUE) { printf("eglChooseConfig failed\n"); - return 0;
+ return 0; } #endif - surface = eglCreateWindowSurface(dpy, configs[0], window, NULL); - checkEglError("eglCreateWindowSurface"); - if (surface == EGL_NO_SURFACE) - { - printf("gelCreateWindowSurface failed.\n"); - return 0; - } - EGLint gl2_0Attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - - context = eglCreateContext(dpy, configs[0], EGL_NO_CONTEXT, context_attribs); - checkEglError("eglCreateContext"); - if (context == EGL_NO_CONTEXT) - { + surface = eglCreateWindowSurface(dpy, configs[0], window, NULL); + checkEglError("eglCreateWindowSurface"); + if (surface == EGL_NO_SURFACE) { + printf("gelCreateWindowSurface failed.\n"); + return 0; + } + EGLint gl2_0Attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + + context = eglCreateContext(dpy, configs[0], EGL_NO_CONTEXT, context_attribs); + checkEglError("eglCreateContext"); + if (context == EGL_NO_CONTEXT) { printf("eglCreateContext failed\n"); return 0; - } - eglMakeCurrent(dpy, surface, surface, context); - checkEglError("eglMakeCurrent"); - eglQuerySurface(dpy, surface, EGL_WIDTH, &w); - checkEglError("eglQuerySurface"); - eglQuerySurface(dpy, surface, EGL_HEIGHT, &h); - checkEglError("eglQuerySurface"); - GLint dim = w<h ? w : h; - - fprintf(stderr, "Window dimensions: %d x %d\n", w, h); - - printGLString("Version", GL_VERSION); - printGLString("Vendor", GL_VENDOR); - printGLString("Renderer", GL_RENDERER); - printGLString("Extensions", GL_EXTENSIONS); - - return 0; + } + eglMakeCurrent(dpy, surface, surface, context); + checkEglError("eglMakeCurrent"); + eglQuerySurface(dpy, surface, EGL_WIDTH, &w); + checkEglError("eglQuerySurface"); + eglQuerySurface(dpy, surface, EGL_HEIGHT, &h); + checkEglError("eglQuerySurface"); + GLint dim = w < h ? w : h; + + fprintf(stderr, "Window dimensions: %d x %d\n", w, h); + + printGLString("Version", GL_VERSION); + printGLString("Vendor", GL_VENDOR); + printGLString("Renderer", GL_RENDERER); + printGLString("Extensions", GL_EXTENSIONS); + + if(!setupGraphics(w, h)) { + fprintf(stderr, "Could not set up graphics.\n"); + return 0; + } + + for (;;) { + renderFrame(); + eglSwapBuffers(dpy, surface); + } + + return 0; } diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index d5f1c61..db1b5f1 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -29,7 +29,7 @@ <integer name="def_screen_brightness">102</integer> <bool name="def_screen_brightness_automatic_mode">false</bool> <fraction name="def_window_animation_scale">100%</fraction> - <fraction name="def_window_transition_scale">0%</fraction> + <fraction name="def_window_transition_scale">100%</fraction> <bool name="def_bluetooth_on">false</bool> <bool name="def_install_non_market_apps">false</bool> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index f99eb58..c561078 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -71,7 +71,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 40; + private static final int DATABASE_VERSION = 41; private Context mContext; @@ -481,6 +481,27 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 40; } + if (upgradeVersion == 40) { + /* + * All animations are now turned on by default! + */ + db.beginTransaction(); + try { + db.execSQL("DELETE FROM system WHERE name='" + + Settings.System.WINDOW_ANIMATION_SCALE + "'"); + db.execSQL("DELETE FROM system WHERE name='" + + Settings.System.TRANSITION_ANIMATION_SCALE + "'"); + SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)" + + " VALUES(?,?);"); + loadDefaultAnimationSettings(stmt); + stmt.close(); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + upgradeVersion = 41; + } + if (upgradeVersion != currentVersion) { Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion + ", must wipe the settings provider"); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 8cfd956..fb5e4e6 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -50,19 +50,22 @@ import android.util.Log; * List of settings that are backed up are stored in the Settings.java file */ public class SettingsBackupAgent extends BackupHelperAgent { + // STOPSHIP: set DEBUG to false private static final boolean DEBUG = true; private static final String KEY_SYSTEM = "system"; private static final String KEY_SECURE = "secure"; - private static final String KEY_SYNC = "sync_providers"; private static final String KEY_LOCALE = "locale"; + // Versioning of the state file. Increment this version + // number any time the set of state items is altered. + private static final int STATE_VERSION = 1; + private static final int STATE_SYSTEM = 0; private static final int STATE_SECURE = 1; - private static final int STATE_SYNC = 2; - private static final int STATE_LOCALE = 3; - private static final int STATE_WIFI = 4; - private static final int STATE_SIZE = 5; // The number of state items + private static final int STATE_LOCALE = 2; + private static final int STATE_WIFI = 3; + private static final int STATE_SIZE = 4; // The number of state items private static String[] sortedSystemKeys = null; private static String[] sortedSecureKeys = null; @@ -101,7 +104,6 @@ public class SettingsBackupAgent extends BackupHelperAgent { byte[] systemSettingsData = getSystemSettings(); byte[] secureSettingsData = getSecureSettings(); - byte[] syncProviders = mSettingsHelper.getSyncProviders(); byte[] locale = mSettingsHelper.getLocaleData(); byte[] wifiData = getWifiSupplicant(FILE_WIFI_SUPPLICANT); @@ -111,8 +113,6 @@ public class SettingsBackupAgent extends BackupHelperAgent { writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data); stateChecksums[STATE_SECURE] = writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data); - stateChecksums[STATE_SYNC] = - writeIfChanged(stateChecksums[STATE_SYNC], KEY_SYNC, syncProviders, data); stateChecksums[STATE_LOCALE] = writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data); stateChecksums[STATE_WIFI] = @@ -143,8 +143,6 @@ public class SettingsBackupAgent extends BackupHelperAgent { // retain the previous WIFI state. enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED || retainedWifiState == WifiManager.WIFI_STATE_ENABLING); - } else if (KEY_SYNC.equals(key)) { - mSettingsHelper.setSyncProviders(data); } else if (KEY_LOCALE.equals(key)) { byte[] localeData = new byte[size]; data.readEntityData(localeData, 0, size); @@ -160,12 +158,17 @@ public class SettingsBackupAgent extends BackupHelperAgent { DataInputStream dataInput = new DataInputStream( new FileInputStream(oldState.getFileDescriptor())); - for (int i = 0; i < STATE_SIZE; i++) { - try { - stateChecksums[i] = dataInput.readLong(); - } catch (EOFException eof) { - break; + + try { + int stateVersion = dataInput.readInt(); + if (stateVersion == STATE_VERSION) { + for (int i = 0; i < STATE_SIZE; i++) { + stateChecksums[i] = dataInput.readLong(); + } } + } catch (EOFException eof) { + // With the default 0 checksum we'll wind up forcing a backup of + // any unhandled data sets, which is appropriate. } dataInput.close(); return stateChecksums; @@ -175,6 +178,8 @@ public class SettingsBackupAgent extends BackupHelperAgent { throws IOException { DataOutputStream dataOutput = new DataOutputStream( new FileOutputStream(newState.getFileDescriptor())); + + dataOutput.writeInt(STATE_VERSION); for (int i = 0; i < STATE_SIZE; i++) { dataOutput.writeLong(checksums[i]); } @@ -223,6 +228,14 @@ public class SettingsBackupAgent extends BackupHelperAgent { } private void restoreSettings(BackupDataInput data, Uri contentUri) { + if (DEBUG) Log.i(TAG, "restoreSettings: " + contentUri); + String[] whitelist = null; + if (contentUri.equals(Settings.Secure.CONTENT_URI)) { + whitelist = Settings.Secure.SETTINGS_TO_BACKUP; + } else if (contentUri.equals(Settings.System.CONTENT_URI)) { + whitelist = Settings.System.SETTINGS_TO_BACKUP; + } + ContentValues cv = new ContentValues(2); byte[] settings = new byte[data.getDataSize()]; try { @@ -244,9 +257,8 @@ public class SettingsBackupAgent extends BackupHelperAgent { if (!TextUtils.isEmpty(settingName) && !TextUtils.isEmpty(settingValue)) { //Log.i(TAG, "Restore " + settingName + " = " + settingValue); - // TODO: versioning rather than just an ad hoc blacklist to handle - // older varieties of backed-up data - if (invalidSavedSetting(contentUri, settingName, settingValue)) { + // Only restore settings in our list of known-acceptable data + if (invalidSavedSetting(whitelist, settingName)) { continue; } @@ -260,20 +272,23 @@ public class SettingsBackupAgent extends BackupHelperAgent { } } - private boolean invalidSavedSetting(Uri contentUri, String settingName, String settingValue) { - // Even if these settings were stored, don't use them on restore - if (contentUri.equals(Settings.Secure.CONTENT_URI)) { - if (settingName.equals(Settings.Secure.PREFERRED_NETWORK_MODE) - || settingName.equals(Settings.Secure.PREFERRED_TTY_MODE) - || settingName.equals(Settings.Secure.CDMA_CELL_BROADCAST_SMS) - || settingName.equals(Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION) - || settingName.equals(Settings.Secure.ENHANCED_VOICE_PRIVACY_ENABLED)) { - if (DEBUG) Log.v(TAG, "Ignoring restore datum: " + settingName); - return true; + // Returns 'true' if the given setting is one that we refuse to restore + private boolean invalidSavedSetting(String[] knownNames, String candidate) { + // no filter? allow everything + if (knownNames == null) { + return false; + } + + // whitelisted setting? allow it + for (String name : knownNames) { + if (name.equals(candidate)) { + return false; } } - return false; + // refuse everything else + if (DEBUG) Log.v(TAG, "Ignoring restore datum: " + candidate); + return true; } private String[] copyAndSort(String[] keys) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 39084a7..77da8f1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -116,43 +116,6 @@ public class SettingsHelper { } } - byte[] getSyncProviders() { - byte[] sync = new byte[1 + PROVIDERS.length]; - // TODO: Sync backup needs to be moved to SystemBackupAgent - /* - try { - sync[0] = (byte) (mContentService.getListenForNetworkTickles() ? 1 : 0); - for (int i = 0; i < PROVIDERS.length; i++) { - sync[i + 1] = (byte) - (mContentService.getSyncAutomatically(PROVIDERS[i]) ? 1 : 0); - } - } catch (RemoteException re) { - Log.w(TAG, "Unable to backup sync providers"); - return sync; - } - */ - return sync; - } - - void setSyncProviders(BackupDataInput backup) { - byte[] sync = new byte[backup.getDataSize()]; - - try { - backup.readEntityData(sync, 0, sync.length); - // TODO: Sync backup needs to be moved to SystemBackupAgent - /* - mContentService.setListenForNetworkTickles(sync[0] == 1); - for (int i = 0; i < PROVIDERS.length; i++) { - mContentService.setSyncProviderAutomatically(PROVIDERS[i], sync[i + 1] > 0); - } - } catch (RemoteException re) { - Log.w(TAG, "Unable to restore sync providers"); - */ - } catch (java.io.IOException ioe) { - Log.w(TAG, "Unable to read sync settings"); - } - } - byte[] getLocaleData() { Configuration conf = mContext.getResources().getConfiguration(); final Locale loc = conf.locale; diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java index 13b4952..9909905 100644 --- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java @@ -17,7 +17,7 @@ package com.android.server.vpn; import android.net.vpn.L2tpIpsecProfile; -import android.security.CertTool; +import android.security.Credentials; import java.io.IOException; @@ -30,16 +30,18 @@ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { @Override protected void connect(String serverIp, String username, String password) throws IOException { + L2tpIpsecProfile p = getProfile(); // IPSEC DaemonProxy ipsec = startDaemon(IPSEC); ipsec.sendCommand(serverIp, L2tpService.L2TP_PORT, - getUserkeyPath(), getUserCertPath(), getCaCertPath()); + Credentials.USER_PRIVATE_KEY + p.getUserCertificate(), + Credentials.USER_CERTIFICATE + p.getUserCertificate(), + Credentials.CA_CERTIFICATE + p.getCaCertificate()); ipsec.closeControlSocket(); sleep(2000); // 2 seconds // L2TP - L2tpIpsecProfile p = getProfile(); MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp, L2tpService.L2TP_PORT, (p.isSecretEnabled() ? p.getSecretString() : null), @@ -51,19 +53,4 @@ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { stopDaemon(IPSEC); stopDaemon(MtpdHelper.MTPD); } - - private String getCaCertPath() { - return CertTool.getInstance().getCaCertificate( - getProfile().getCaCertificate()); - } - - private String getUserCertPath() { - return CertTool.getInstance().getUserCertificate( - getProfile().getUserCertificate()); - } - - private String getUserkeyPath() { - return CertTool.getInstance().getUserPrivateKey( - getProfile().getUserCertificate()); - } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index c6be61d..2fa18bf 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -57,6 +57,7 @@ import android.backup.IRestoreObserver; import android.backup.IRestoreSession; import android.backup.RestoreSet; +import com.android.internal.backup.BackupConstants; import com.android.internal.backup.LocalTransport; import com.android.internal.backup.IBackupTransport; @@ -101,6 +102,7 @@ class BackupManagerService extends IBackupManager.Stub { private static final int BACKUP_AGENT_FAILURE_EVENT = 2823; private static final int BACKUP_PACKAGE_EVENT = 2824; private static final int BACKUP_SUCCESS_EVENT = 2825; + private static final int BACKUP_RESET_EVENT = 2826; private static final int RESTORE_START_EVENT = 2830; private static final int RESTORE_TRANSPORT_FAILURE_EVENT = 2831; @@ -406,6 +408,47 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Reset all of our bookkeeping, in response to having been told that + // the backend data has been wiped [due to idle expiry, for example], + // so we must re-upload all saved settings. + void resetBackupState(File stateFileDir) { + synchronized (mQueueLock) { + // Wipe the "what we've ever backed up" tracking + try { + // close the ever-stored journal... + if (mEverStoredStream != null) { + mEverStoredStream.close(); + } + // ... so we can delete it and start over + mEverStored.delete(); + mEverStoredStream = new RandomAccessFile(mEverStored, "rwd"); + } catch (IOException e) { + Log.e(TAG, "Unable to open known-stored file!"); + mEverStoredStream = null; + } + mEverStoredApps.clear(); + + // Remove all the state files + for (File sf : stateFileDir.listFiles()) { + sf.delete(); + } + + // Enqueue a new backup of every participant + int N = mBackupParticipants.size(); + for (int i=0; i<N; i++) { + int uid = mBackupParticipants.keyAt(i); + HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i); + for (ApplicationInfo app: participants) { + try { + dataChanged(app.packageName); + } catch (RemoteException e) { + // can't happen; we're in the same process + } + } + } + } + } + // Add a transport to our set of available backends private void registerTransport(String name, IBackupTransport transport) { synchronized (mTransports) { @@ -869,6 +912,7 @@ class BackupManagerService extends IBackupManager.Stub { @Override public void run() { + int status = BackupConstants.TRANSPORT_OK; long startRealtime = SystemClock.elapsedRealtime(); if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); @@ -878,43 +922,83 @@ class BackupManagerService extends IBackupManager.Stub { try { EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName()); + // If we haven't stored anything yet, we need to do an init operation. + if (status == BackupConstants.TRANSPORT_OK && mEverStoredApps.size() == 0) { + status = mTransport.initializeDevice(); + } + // The package manager doesn't have a proper <application> etc, but since // it's running here in the system process we can just set up its agent // directly and use a synthetic BackupRequest. We always run this pass // because it's cheap and this way we guarantee that we don't get out of // step even if we're selecting among various transports at run time. - PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( - mPackageManager, allAgentPackages()); - BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false); - pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; - processOneBackup(pmRequest, - IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); - - // Now run all the backups in our queue - int count = mQueue.size(); - doQueuedBackups(mTransport); - - // Finally, tear down the transport - if (mTransport.finishBackup()) { - int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); - EventLog.writeEvent(BACKUP_SUCCESS_EVENT, count, millis); - } else { - EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, ""); - Log.e(TAG, "Transport error in finishBackup()"); + if (status == BackupConstants.TRANSPORT_OK) { + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + mPackageManager, allAgentPackages()); + BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false); + pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; + status = processOneBackup(pmRequest, + IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); } - if (!mJournal.delete()) { - Log.e(TAG, "Unable to remove backup journal file " + mJournal); + if (status == BackupConstants.TRANSPORT_OK) { + // Now run all the backups in our queue + status = doQueuedBackups(mTransport); + } + + if (status == BackupConstants.TRANSPORT_OK) { + // Tell the transport to finish everything it has buffered + status = mTransport.finishBackup(); + if (status == BackupConstants.TRANSPORT_OK) { + int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); + EventLog.writeEvent(BACKUP_SUCCESS_EVENT, mQueue.size(), millis); + } else { + EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, ""); + Log.e(TAG, "Transport error in finishBackup()"); + } + } + + if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) { + // The backend reports that our dataset has been wiped. We need to + // reset all of our bookkeeping and instead run a new backup pass for + // everything. + EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName()); + resetBackupState(mStateDir); + backupNow(); } } catch (Exception e) { Log.e(TAG, "Error in backup thread", e); + status = BackupConstants.TRANSPORT_ERROR; } finally { + // If things went wrong, we need to re-stage the apps we had expected + // to be backing up in this pass. This journals the package names in + // the current active pending-backup file, not in the we are holding + // here in mJournal. + if (status != BackupConstants.TRANSPORT_OK) { + Log.w(TAG, "Backup pass unsuccessful, restaging"); + for (BackupRequest req : mQueue) { + try { + dataChanged(req.appInfo.packageName); + } catch (RemoteException e) { + // can't happen; it's a local call + } + } + } + + // Either backup was successful, in which case we of course do not need + // this pass's journal any more; or it failed, in which case we just + // re-enqueued all of these packages in the current active journal. + // Either way, we no longer need this pass's journal. + if (!mJournal.delete()) { + Log.e(TAG, "Unable to remove backup journal file " + mJournal); + } + // Only once we're entirely finished do we release the wakelock mWakelock.release(); } } - private void doQueuedBackups(IBackupTransport transport) { + private int doQueuedBackups(IBackupTransport transport) { for (BackupRequest request : mQueue) { Log.d(TAG, "starting agent for backup of " + request); @@ -934,26 +1018,27 @@ class BackupManagerService extends IBackupManager.Stub { try { agent = bindToAgentSynchronous(request.appInfo, mode); if (agent != null) { - processOneBackup(request, agent, transport); + int result = processOneBackup(request, agent, transport); + if (result != BackupConstants.TRANSPORT_OK) return result; } - - // unbind even on timeout, just in case - mActivityManager.unbindBackupAgent(request.appInfo); } catch (SecurityException ex) { // Try for the next one. Log.d(TAG, "error in bind/backup", ex); - } catch (RemoteException e) { - Log.v(TAG, "bind/backup threw"); - e.printStackTrace(); + } finally { + try { // unbind even on timeout, just in case + mActivityManager.unbindBackupAgent(request.appInfo); + } catch (RemoteException e) {} } } + + return BackupConstants.TRANSPORT_OK; } - void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) { + private int processOneBackup(BackupRequest request, IBackupAgent agent, + IBackupTransport transport) { final String packageName = request.appInfo.packageName; if (DEBUG) Log.d(TAG, "processOneBackup doBackup() on " + packageName); - // !!! TODO: get the state file dir from the transport File savedStateName = new File(mStateDir, packageName); File backupDataName = new File(mDataDir, packageName + ".data"); File newStateName = new File(mStateDir, packageName + ".new"); @@ -1003,7 +1088,7 @@ class BackupManagerService extends IBackupManager.Stub { EventLog.writeEvent(BACKUP_AGENT_FAILURE_EVENT, packageName, e.toString()); backupDataName.delete(); newStateName.delete(); - return; + return BackupConstants.TRANSPORT_ERROR; } finally { try { if (savedState != null) savedState.close(); } catch (IOException e) {} try { if (backupData != null) backupData.close(); } catch (IOException e) {} @@ -1012,13 +1097,24 @@ class BackupManagerService extends IBackupManager.Stub { } // Now propagate the newly-backed-up data to the transport + int result = BackupConstants.TRANSPORT_OK; try { int size = (int) backupDataName.length(); if (size > 0) { - backupData = ParcelFileDescriptor.open(backupDataName, - ParcelFileDescriptor.MODE_READ_ONLY); + if (result == BackupConstants.TRANSPORT_OK) { + backupData = ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_ONLY); + result = transport.performBackup(packInfo, backupData); + } + + // TODO - We call finishBackup() for each application backed up, because + // we need to know now whether it succeeded or failed. Instead, we should + // hold off on finishBackup() until the end, which implies holding off on + // renaming *all* the output state files (see below) until that happens. - if (!transport.performBackup(packInfo, backupData)) throw new Exception(); + if (result == BackupConstants.TRANSPORT_OK) { + result = transport.finishBackup(); + } } else { if (DEBUG) Log.i(TAG, "no backup data written; not calling transport"); } @@ -1026,16 +1122,22 @@ class BackupManagerService extends IBackupManager.Stub { // After successful transport, delete the now-stale data // and juggle the files so that next time we supply the agent // with the new state file it just created. - backupDataName.delete(); - newStateName.renameTo(savedStateName); - EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size); + if (result == BackupConstants.TRANSPORT_OK) { + backupDataName.delete(); + newStateName.renameTo(savedStateName); + EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size); + } else { + EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName); + } } catch (Exception e) { Log.e(TAG, "Transport error backing up " + packageName, e); EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName); - return; + result = BackupConstants.TRANSPORT_ERROR; } finally { try { if (backupData != null) backupData.close(); } catch (IOException e) {} } + + return result; } } @@ -1136,7 +1238,7 @@ class BackupManagerService extends IBackupManager.Stub { // build the set of apps to restore try { // TODO: Log this before getAvailableRestoreSets, somehow - EventLog.writeEvent(RESTORE_START_EVENT, mTransport.transportDirName()); + EventLog.writeEvent(RESTORE_START_EVENT, mTransport.transportDirName(), mToken); // Get the list of all packages which have backup enabled. // (Include the Package Manager metadata pseudo-package first.) @@ -1160,7 +1262,8 @@ class BackupManagerService extends IBackupManager.Stub { } } - if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) { + if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) != + BackupConstants.TRANSPORT_OK) { Log.e(TAG, "Error starting restore operation"); EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT); return; @@ -1360,7 +1463,7 @@ class BackupManagerService extends IBackupManager.Stub { ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE); - if (!mTransport.getRestoreData(backupData)) { + if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) { Log.e(TAG, "Error getting restore data for " + packageName); EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT); return; @@ -1379,7 +1482,21 @@ class BackupManagerService extends IBackupManager.Stub { agent.doRestore(backupData, appVersionCode, newState); // if everything went okay, remember the recorded state now - newStateName.renameTo(savedStateName); + // + // !!! TODO: the restored data should be migrated on the server + // side into the current dataset. In that case the new state file + // we just created would reflect the data already extant in the + // backend, so there'd be nothing more to do. Until that happens, + // however, we need to make sure that we record the data to the + // current backend dataset. (Yes, this means shipping the data over + // the wire in both directions. That's bad, but consistency comes + // first, then efficiency.) Once we introduce server-side data + // migration to the newly-restored device's dataset, we will change + // the following from a discard of the newly-written state to the + // "correct" operation of renaming into the canonical state blob. + newStateName.delete(); // TODO: remove; see above comment + //newStateName.renameTo(savedStateName); // TODO: replace with this + int size = (int) backupDataName.length(); EventLog.writeEvent(RESTORE_PACKAGE_EVENT, packageName, size); } catch (Exception e) { @@ -1417,11 +1534,13 @@ class BackupManagerService extends IBackupManager.Stub { stateFile.delete(); // Tell the transport to remove all the persistent storage for the app + // STOPSHIP TODO - need to handle failures mTransport.clearBackupData(mPackage); } catch (RemoteException e) { // can't happen; the transport is local } finally { try { + // STOPSHIP TODO - need to handle failures mTransport.finishBackup(); } catch (RemoteException e) { // can't happen; the transport is local @@ -1562,7 +1681,6 @@ class BackupManagerService extends IBackupManager.Stub { if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass"); synchronized (mQueueLock) { try { - if (DEBUG) Log.v(TAG, "sending immediate backup broadcast"); mRunBackupIntent.send(); } catch (PendingIntent.CanceledException e) { // should never happen diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index 26fee89..d78d886 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -26,7 +26,6 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.BatteryManager; import android.os.Binder; -import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -333,16 +332,16 @@ class BatteryService extends Binder { int icon = getIcon(mBatteryLevel); - intent.putExtra("status", mBatteryStatus); - intent.putExtra("health", mBatteryHealth); - intent.putExtra("present", mBatteryPresent); - intent.putExtra("level", mBatteryLevel); - intent.putExtra("scale", BATTERY_SCALE); - intent.putExtra("icon-small", icon); - intent.putExtra("plugged", mPlugType); - intent.putExtra("voltage", mBatteryVoltage); - intent.putExtra("temperature", mBatteryTemperature); - intent.putExtra("technology", mBatteryTechnology); + intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryStatus); + intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth); + intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryPresent); + intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryLevel); + intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE); + intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType); + intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage); + intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature); + intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology); if (false) { Log.d(TAG, "updateBattery level:" + mBatteryLevel + diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index 60195b9..a70134d 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -16,7 +16,9 @@ package com.android.server; +import android.app.Activity; import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Handler; @@ -44,6 +46,44 @@ class DockObserver extends UEventObserver { private final Context mContext; private PowerManagerService mPowerManager; + + // The broadcast receiver which receives the result of the ordered broadcast sent when + // the dock state changes. The original ordered broadcast is sent with an initial result + // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g., + // to RESULT_CANCELED, then the intent to start a dock app will not be sent. + private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (getResultCode() != Activity.RESULT_OK) { + return; + } + + // Launch a dock activity + String category; + switch (mDockState) { + case Intent.EXTRA_DOCK_STATE_CAR: + category = Intent.CATEGORY_CAR_DOCK; + break; + case Intent.EXTRA_DOCK_STATE_DESK: + category = Intent.CATEGORY_DESK_DOCK; + break; + default: + category = null; + break; + } + if (category != null) { + intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(category); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, e.getCause()); + } + } + } + }; public DockObserver(Context context, PowerManagerService pm) { mContext = context; @@ -111,31 +151,15 @@ class DockObserver extends UEventObserver { mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(), false, true); Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); - mContext.sendStickyBroadcast(intent); - - // Launch a dock activity - String category; - switch (mDockState) { - case Intent.EXTRA_DOCK_STATE_CAR: - category = Intent.CATEGORY_CAR_DOCK; - break; - case Intent.EXTRA_DOCK_STATE_DESK: - category = Intent.CATEGORY_DESK_DOCK; - break; - default: - category = null; - break; - } - if (category != null) { - intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(category); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - mContext.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(TAG, e.getCause()); - } - } + + // Send the ordered broadcast; the result receiver will receive after all + // broadcasts have been sent. If any broadcast receiver changes the result + // code from the initial value of RESULT_OK, then the result receiver will + // not launch the corresponding dock application. This gives apps a chance + // to override the behavior and stay in their app even when the device is + // placed into a dock. + mContext.sendStickyOrderedBroadcast( + intent, mResultReceiver, null, Activity.RESULT_OK, null, null); } } }; diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 84250bc..b2e3a8c 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -304,10 +304,7 @@ class PowerManagerService extends IPowerManager.Stub // temporarily set mUserActivityAllowed to true so this will work // even when the keyguard is on. synchronized (mLocks) { - boolean savedActivityAllowed = mUserActivityAllowed; - mUserActivityAllowed = true; - userActivity(SystemClock.uptimeMillis(), false); - mUserActivityAllowed = savedActivityAllowed; + forceUserActivityLocked(); } } } @@ -1714,6 +1711,13 @@ class PowerManagerService extends IPowerManager.Stub } } + private void forceUserActivityLocked() { + boolean savedActivityAllowed = mUserActivityAllowed; + mUserActivityAllowed = true; + userActivity(SystemClock.uptimeMillis(), false); + mUserActivityAllowed = savedActivityAllowed; + } + public void userActivityWithForce(long time, boolean noChangeLights, boolean force) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); userActivity(time, noChangeLights, OTHER_EVENT, force); @@ -1858,9 +1862,10 @@ class PowerManagerService extends IPowerManager.Stub Log.d(TAG, "setKeyboardVisibility: " + visible); } mKeyboardVisible = visible; - // don't signal user activity when closing keyboard if the screen is off. - // otherwise, we want to make sure the backlights are adjusted. - if (visible || (mPowerState & SCREEN_ON_BIT) != 0) { + // don't signal user activity if the screen is off; other code + // will take care of turning on due to a true change to the lid + // switch and synchronized with the lock screen. + if ((mPowerState & SCREEN_ON_BIT) != 0) { userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true); } } @@ -2114,14 +2119,21 @@ class PowerManagerService extends IPowerManager.Stub Log.d(TAG, "disableProximityLockLocked"); } mSensorManager.unregisterListener(this); - mProximitySensorActive = false; + synchronized (mLocks) { + if (mProximitySensorActive) { + mProximitySensorActive = false; + forceUserActivityLocked(); + } + } } public void onSensorChanged(SensorEvent event) { long milliseconds = event.timestamp / 1000000; synchronized (mLocks) { float distance = event.values[0]; - if (distance >= 0.0 && distance < PROXIMITY_THRESHOLD) { + // compare against getMaximumRange to support sensors that only return 0 or 1 + if (distance >= 0.0 && distance < PROXIMITY_THRESHOLD && + distance < mProximitySensor.getMaximumRange()) { if (mSpew) { Log.d(TAG, "onSensorChanged: proximity active, distance: " + distance); } @@ -2135,10 +2147,7 @@ class PowerManagerService extends IPowerManager.Stub Log.d(TAG, "onSensorChanged: proximity inactive, distance: " + distance); } mProximitySensorActive = false; - boolean savedActivityAllowed = mUserActivityAllowed; - mUserActivityAllowed = true; - userActivity(milliseconds, false); - mUserActivityAllowed = savedActivityAllowed; + forceUserActivityLocked(); } } } diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java index af80e20..ac3b723 100644 --- a/services/java/com/android/server/ProcessStats.java +++ b/services/java/com/android/server/ProcessStats.java @@ -30,6 +30,7 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.StringTokenizer; public class ProcessStats { private static final String TAG = "ProcessStats"; @@ -138,7 +139,22 @@ public class ProcessStats { private boolean mFirst = true; private byte[] mBuffer = new byte[256]; - + + /** + * The time in microseconds that the CPU has been running at each speed. + */ + private long[] mCpuSpeedTimes; + + /** + * The relative time in microseconds that the CPU has been running at each speed. + */ + private long[] mRelCpuSpeedTimes; + + /** + * The different speeds that the CPU can be running at. + */ + private long[] mCpuSpeeds; + public static class Stats { public final int pid; final String statFile; @@ -460,6 +476,70 @@ public class ProcessStats { return 0; } + /** + * Returns the times spent at each CPU speed, since the last call to this method. If this + * is the first time, it will return 1 for each value. + * @return relative times spent at different speed steps. + */ + public long[] getLastCpuSpeedTimes() { + if (mCpuSpeedTimes == null) { + mCpuSpeedTimes = getCpuSpeedTimes(null); + mRelCpuSpeedTimes = new long[mCpuSpeedTimes.length]; + for (int i = 0; i < mCpuSpeedTimes.length; i++) { + mRelCpuSpeedTimes[i] = 1; // Initialize + } + } else { + getCpuSpeedTimes(mRelCpuSpeedTimes); + for (int i = 0; i < mCpuSpeedTimes.length; i++) { + long temp = mRelCpuSpeedTimes[i]; + mRelCpuSpeedTimes[i] -= mCpuSpeedTimes[i]; + mCpuSpeedTimes[i] = temp; + } + } + return mRelCpuSpeedTimes; + } + + private long[] getCpuSpeedTimes(long[] out) { + long[] tempTimes = out; + long[] tempSpeeds = mCpuSpeeds; + final int MAX_SPEEDS = 20; + if (out == null) { + tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that + tempSpeeds = new long[MAX_SPEEDS]; + } + int speed = 0; + String file = readFile("/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state", '\0'); + // Note: file may be null on kernels without cpufreq (i.e. the emulator's) + if (file != null) { + StringTokenizer st = new StringTokenizer(file, "\n "); + while (st.hasMoreElements()) { + String token = st.nextToken(); + try { + long val = Long.parseLong(token); + tempSpeeds[speed] = val; + token = st.nextToken(); + val = Long.parseLong(token); + tempTimes[speed] = val; + speed++; + if (speed == MAX_SPEEDS) break; // No more + if (localLOGV && out == null) { + Log.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1] + + "\t" + tempTimes[speed - 1]); + } + } catch (NumberFormatException nfe) { + Log.i(TAG, "Unable to parse time_in_state"); + } + } + } + if (out == null) { + out = new long[speed]; + mCpuSpeeds = new long[speed]; + System.arraycopy(tempSpeeds, 0, mCpuSpeeds, 0, speed); + System.arraycopy(tempTimes, 0, out, 0, speed); + } + return out; + } + final public int getLastUserTime() { return mRelUserTime; } diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java index 17d0f1d..8903ebd 100644 --- a/services/java/com/android/server/SystemBackupAgent.java +++ b/services/java/com/android/server/SystemBackupAgent.java @@ -32,23 +32,33 @@ import java.io.File; import java.io.IOException; /** - * Backup agent for various system-managed data + * Backup agent for various system-managed data, currently just the system wallpaper */ public class SystemBackupAgent extends BackupHelperAgent { private static final String TAG = "SystemBackupAgent"; + // These paths must match what the WallpaperManagerService uses private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper"; private static final String WALLPAPER_INFO = "/data/system/wallpaper_info.xml"; @Override - public void onCreate() { + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) throws IOException { + // We only back up the data under the current "wallpaper" schema with metadata addHelper("wallpaper", new AbsoluteFileBackupHelper(SystemBackupAgent.this, new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO })); + super.onBackup(oldState, data, newState); } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { + // On restore, we also support a previous data schema "system_files" + addHelper("wallpaper", new AbsoluteFileBackupHelper(SystemBackupAgent.this, + new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO })); + addHelper("system_files", new AbsoluteFileBackupHelper(SystemBackupAgent.this, + new String[] { WALLPAPER_IMAGE })); + boolean success = false; try { super.onRestore(data, appVersionCode, newState); diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 63bef54..cc1b697 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -295,7 +295,11 @@ public class WifiService extends IWifiManager.Stub { if (mWifiHandler == null) return false; synchronized (mWifiHandler) { + // caller may not have WAKE_LOCK permission - it's not required here + long ident = Binder.clearCallingIdentity(); sWakeLock.acquire(); + Binder.restoreCallingIdentity(ident); + mLastEnableUid = Binder.getCallingUid(); // set a flag if the user is enabling Wifi while in airplane mode mAirplaneModeOverwridden = (enable && isAirplaneModeOn() && isAirplaneToggleable()); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 0cd5949..38d2304 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -143,6 +143,7 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean DEBUG_REORDER = false; static final boolean DEBUG_WALLPAPER = false; static final boolean SHOW_TRANSACTIONS = false; + static final boolean HIDE_STACK_CRAWLS = true; static final boolean MEASURE_LATENCY = false; static private LatencyTimer lt; @@ -372,7 +373,7 @@ public class WindowManagerService extends IWindowManager.Stub // perform or TRANSIT_NONE if we are not waiting. If we are waiting, // mOpeningApps and mClosingApps are the lists of tokens that will be // made visible or hidden at the next transition. - int mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE; + int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; boolean mAppTransitionReady = false; boolean mAppTransitionRunning = false; boolean mAppTransitionTimeout = false; @@ -622,7 +623,7 @@ public class WindowManagerService extends IWindowManager.Stub private void placeWindowAfter(Object pos, WindowState window) { final int i = mWindows.indexOf(pos); - if (localLOGV || DEBUG_FOCUS) Log.v( + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( TAG, "Adding window " + window + " at " + (i+1) + " of " + mWindows.size() + " (after " + pos + ")"); mWindows.add(i+1, window); @@ -630,7 +631,7 @@ public class WindowManagerService extends IWindowManager.Stub private void placeWindowBefore(Object pos, WindowState window) { final int i = mWindows.indexOf(pos); - if (localLOGV || DEBUG_FOCUS) Log.v( + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( TAG, "Adding window " + window + " at " + i + " of " + mWindows.size() + " (before " + pos + ")"); mWindows.add(i, window); @@ -687,6 +688,9 @@ public class WindowManagerService extends IWindowManager.Stub //apptoken note that the window could be a floating window //that was created later or a window at the top of the list of //windows associated with this token. + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + TAG, "Adding window " + win + " at " + + (newIdx+1) + " of " + N); localmWindows.add(newIdx+1, win); } } @@ -766,9 +770,9 @@ public class WindowManagerService extends IWindowManager.Stub break; } } - if (localLOGV || DEBUG_FOCUS) Log.v( - TAG, "Adding window " + win + " at " - + i + " of " + N); + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + TAG, "Adding window " + win + " at " + + i + " of " + N); localmWindows.add(i, win); } } @@ -783,9 +787,9 @@ public class WindowManagerService extends IWindowManager.Stub } } if (i < 0) i = 0; - if (localLOGV || DEBUG_FOCUS) Log.v( - TAG, "Adding window " + win + " at " - + i + " of " + N); + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + TAG, "Adding window " + win + " at " + + i + " of " + N); localmWindows.add(i, win); } if (addToToken) { @@ -932,7 +936,7 @@ public class WindowManagerService extends IWindowManager.Stub + " layer=" + highestTarget.mAnimLayer + " new layer=" + w.mAnimLayer); - if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { // If we are currently setting up for an animation, // hold everything until we can find out what will happen. mInputMethodTargetWaitingAnim = true; @@ -955,7 +959,7 @@ public class WindowManagerService extends IWindowManager.Stub if (w != null) { if (willMove) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " + mInputMethodTarget + " to " + w, e); mInputMethodTarget = w; @@ -969,7 +973,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (willMove) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " + mInputMethodTarget + " to null", e); mInputMethodTarget = null; @@ -982,6 +986,8 @@ public class WindowManagerService extends IWindowManager.Stub int pos = findDesiredInputMethodWindowIndexLocked(true); if (pos >= 0) { win.mTargetAppToken = mInputMethodTarget.mAppToken; + if (DEBUG_WINDOW_MOVEMENT) Log.v( + TAG, "Adding input method window " + win + " at " + pos); mWindows.add(pos, win); moveInputMethodDialogsLocked(pos+1); return; @@ -1022,6 +1028,7 @@ public class WindowManagerService extends IWindowManager.Stub int wpos = mWindows.indexOf(win); if (wpos >= 0) { if (wpos < interestingPos) interestingPos--; + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Temp removing at " + wpos + ": " + win); mWindows.remove(wpos); int NC = win.mChildWindows.size(); while (NC > 0) { @@ -1030,6 +1037,8 @@ public class WindowManagerService extends IWindowManager.Stub int cpos = mWindows.indexOf(cw); if (cpos >= 0) { if (cpos < interestingPos) interestingPos--; + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Temp removing child at " + + cpos + ": " + cw); mWindows.remove(cpos); } } @@ -1044,6 +1053,8 @@ public class WindowManagerService extends IWindowManager.Stub // this case should be rare, so it shouldn't be that big a deal. int wpos = mWindows.indexOf(win); if (wpos >= 0) { + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "ReAdd removing from " + wpos + + ": " + win); mWindows.remove(wpos); reAddWindowLocked(wpos, win); } @@ -1270,7 +1281,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { // If we are currently waiting for an app transition, and either // the current target or the next target are involved with it, // then hold off on doing anything with the wallpaper. @@ -1472,6 +1483,8 @@ public class WindowManagerService extends IWindowManager.Stub // not in the list. int oldIndex = localmWindows.indexOf(wallpaper); if (oldIndex >= 0) { + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Wallpaper removing at " + + oldIndex + ": " + wallpaper); localmWindows.remove(oldIndex); if (oldIndex < foundI) { foundI--; @@ -1479,7 +1492,8 @@ public class WindowManagerService extends IWindowManager.Stub } // Now stick it in. - if (DEBUG_WALLPAPER) Log.v(TAG, "Moving wallpaper " + wallpaper + if (DEBUG_WALLPAPER || DEBUG_WINDOW_MOVEMENT) Log.v(TAG, + "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + foundI); localmWindows.add(foundI, wallpaper); @@ -2003,6 +2017,7 @@ public class WindowManagerService extends IWindowManager.Stub mWindowMap.remove(win.mClient.asBinder()); mWindows.remove(win); + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Final remove of window: " + win); if (mInputMethodWindow == win) { mInputMethodWindow = null; @@ -2447,7 +2462,7 @@ public class WindowManagerService extends IWindowManager.Stub if (a != null) { if (DEBUG_ANIM) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.v(TAG, "Loaded animation " + a + " for " + win, e); } win.setAnimation(a); @@ -2542,7 +2557,7 @@ public class WindowManagerService extends IWindowManager.Stub : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation; break; } - a = loadAnimation(lp, animAttr); + a = animAttr != 0 ? loadAnimation(lp, animAttr) : null; if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: wtoken=" + wtoken + " anim=" + a + " animAttr=0x" + Integer.toHexString(animAttr) @@ -2551,7 +2566,7 @@ public class WindowManagerService extends IWindowManager.Stub if (a != null) { if (DEBUG_ANIM) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.v(TAG, "Loaded animation " + a + " for " + wtoken, e); } wtoken.setAnimation(a); @@ -2990,7 +3005,8 @@ public class WindowManagerService extends IWindowManager.Stub TAG, "Prepare app transition: transit=" + transit + " mNextAppTransition=" + mNextAppTransition); if (!mDisplayFrozen) { - if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { + if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET + || mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { mNextAppTransition = transit; } else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) { @@ -3025,7 +3041,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { if (DEBUG_APP_TRANSITIONS) Log.v( TAG, "Execute app transition: mNextAppTransition=" + mNextAppTransition); - if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mAppTransitionReady = true; final long origId = Binder.clearCallingIdentity(); performLayoutAndPlaceSurfacesLocked(); @@ -3092,6 +3108,8 @@ public class WindowManagerService extends IWindowManager.Stub startingWindow.mToken = wtoken; startingWindow.mRootToken = wtoken; startingWindow.mAppToken = wtoken; + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, + "Removing starting window: " + startingWindow); mWindows.remove(startingWindow); ttoken.windows.remove(startingWindow); ttoken.allAppWindows.remove(startingWindow); @@ -3228,7 +3246,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean runningAppAnimation = false; - if (transit != WindowManagerPolicy.TRANSIT_NONE) { + if (transit != WindowManagerPolicy.TRANSIT_UNSET) { if (wtoken.animation == sDummyAnimation) { wtoken.animation = null; } @@ -3319,7 +3337,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.v(TAG, "setAppVisibility(" + token + ", " + visible + "): mNextAppTransition=" + mNextAppTransition + " hidden=" + wtoken.hidden @@ -3328,7 +3346,7 @@ public class WindowManagerService extends IWindowManager.Stub // If we are preparing an app transition, then delay changing // the visibility of this token until we execute that transition. - if (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + if (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { // Already in requested state, don't do anything more. if (wtoken.hiddenRequested != visible) { return; @@ -3367,7 +3385,7 @@ public class WindowManagerService extends IWindowManager.Stub } final long origId = Binder.clearCallingIdentity(); - setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_NONE, true); + setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, true); wtoken.updateReportedVisibilityLocked(); Binder.restoreCallingIdentity(origId); } @@ -3411,7 +3429,7 @@ public class WindowManagerService extends IWindowManager.Stub int configChanges) { if (DEBUG_ORIENTATION) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.i(TAG, "Set freezing of " + wtoken.appToken + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.freezingScreen, e); @@ -3493,13 +3511,13 @@ public class WindowManagerService extends IWindowManager.Stub mTokenList.remove(basewtoken); if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) { if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "Removing app token: " + wtoken); - delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_NONE, true); + delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_UNSET, true); wtoken.inPendingTransaction = false; mOpeningApps.remove(wtoken); wtoken.waitingToShow = false; if (mClosingApps.contains(wtoken)) { delayed = true; - } else if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + } else if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mClosingApps.add(wtoken); wtoken.waitingToHide = true; delayed = true; @@ -3511,6 +3529,12 @@ public class WindowManagerService extends IWindowManager.Stub if (delayed) { // set the token aside because it has an active animation to be finished mExitingAppTokens.add(wtoken); + } else { + // Make sure there is no animation running on this token, + // so any windows associated with it will be removed as + // soon as their animations are complete + wtoken.animation = null; + wtoken.animating = false; } mAppTokens.remove(wtoken); wtoken.removed = true; @@ -3546,7 +3570,7 @@ public class WindowManagerService extends IWindowManager.Stub final int NW = token.windows.size(); for (int i=0; i<NW; i++) { WindowState win = token.windows.get(i); - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Tmp removing window " + win); + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Tmp removing app window " + win); mWindows.remove(win); int j = win.mChildWindows.size(); while (j > 0) { @@ -3781,7 +3805,7 @@ public class WindowManagerService extends IWindowManager.Stub AppWindowToken wt = findAppWindowToken(tokens.get(i)); if (wt != null) { mAppTokens.add(wt); - if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mToTopApps.remove(wt); mToBottomApps.remove(wt); mToTopApps.add(wt); @@ -3791,7 +3815,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { + if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) { moveAppWindowsLocked(tokens, mAppTokens.size()); } } @@ -3813,7 +3837,7 @@ public class WindowManagerService extends IWindowManager.Stub AppWindowToken wt = findAppWindowToken(tokens.get(i)); if (wt != null) { mAppTokens.add(pos, wt); - if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mToTopApps.remove(wt); mToBottomApps.remove(wt); mToBottomApps.add(i, wt); @@ -3824,7 +3848,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { + if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) { moveAppWindowsLocked(tokens, 0); } } @@ -6998,13 +7022,13 @@ public class WindowManagerService extends IWindowManager.Stub try { if (DEBUG_VISIBILITY) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.w(TAG, "Window " + this + " destroying surface " + mSurface + ", session " + mSession, e); } if (SHOW_TRANSACTIONS) { RuntimeException ex = new RuntimeException(); - ex.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) ex.fillInStackTrace(); Log.i(TAG, " SURFACE " + mSurface + ": DESTROY (" + mAttrs.getTitle() + ")", ex); } @@ -7059,7 +7083,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean performShowLocked() { if (DEBUG_VISIBILITY) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.v(TAG, "performShow on " + this + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay() + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e); @@ -7319,10 +7343,11 @@ public class WindowManagerService extends IWindowManager.Stub final Matrix tmpMatrix = mTmpMatrix; // Compute the desired transformation. - tmpMatrix.setTranslate(frame.left, frame.top); + tmpMatrix.setTranslate(0, 0); if (selfTransformation) { tmpMatrix.postConcat(mTransformation.getMatrix()); } + tmpMatrix.postTranslate(frame.left, frame.top); if (attachedTransformation != null) { tmpMatrix.postConcat(attachedTransformation.getMatrix()); } @@ -7458,7 +7483,7 @@ public class WindowManagerService extends IWindowManager.Stub */ boolean isReadyForDisplay() { if (mRootToken.waitingToShow && - mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { return false; } final AppWindowToken atoken = mAppToken; @@ -8529,7 +8554,7 @@ public class WindowManagerService extends IWindowManager.Stub case APP_TRANSITION_TIMEOUT: { synchronized (mWindowMap) { - if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "*** APP TRANSITION TIMEOUT"); mAppTransitionReady = true; @@ -8660,6 +8685,7 @@ public class WindowManagerService extends IWindowManager.Stub final void rebuildAppWindowListLocked() { int NW = mWindows.size(); int i; + int numRemoved = 0; // First remove all existing app windows. i=0; @@ -8669,6 +8695,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Rebuild removing window: " + win); NW--; + numRemoved++; continue; } i++; @@ -8689,6 +8716,11 @@ public class WindowManagerService extends IWindowManager.Stub for (int j=0; j<NT; j++) { i = reAddAppWindowsLocked(i, mAppTokens.get(j)); } + + if (i != numRemoved) { + Log.w(TAG, "Rebuild removed " + numRemoved + + " windows but added " + i); + } } private final void assignLayersLocked() { @@ -8851,13 +8883,34 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (!mPolicy.finishLayoutLw()) { + int changes = mPolicy.finishLayoutLw(); + if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) { + if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) { + assignLayersLocked(); + } + } + if (changes == 0) { mLayoutNeeded = false; } else if (repeats > 2) { Log.w(TAG, "Layout repeat aborted after too many iterations"); mLayoutNeeded = false; + if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { + Configuration newConfig = updateOrientationFromAppTokensLocked( + null, null); + if (newConfig != null) { + mLayoutNeeded = true; + mH.sendEmptyMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION); + } + } } else { repeats++; + if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { + Configuration newConfig = updateOrientationFromAppTokensLocked( + null, null); + if (newConfig != null) { + mH.sendEmptyMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION); + } + } } } } @@ -9073,9 +9126,9 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "**** GOOD TO GO"); int transit = mNextAppTransition; if (mSkipAppTransitionAnimation) { - transit = WindowManagerPolicy.TRANSIT_NONE; + transit = WindowManagerPolicy.TRANSIT_UNSET; } - mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE; + mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; mAppTransitionReady = false; mAppTransitionRunning = true; mAppTransitionTimeout = false; @@ -9782,6 +9835,11 @@ public class WindowManagerService extends IWindowManager.Stub for (i=mExitingAppTokens.size()-1; i>=0; i--) { AppWindowToken token = mExitingAppTokens.get(i); if (!token.hasVisible && !mClosingApps.contains(token)) { + // Make sure there is no animation running on this token, + // so any windows associated with it will be removed as + // soon as their animations are complete + token.animation = null; + token.animating = false; mAppTokens.remove(token); mExitingAppTokens.remove(i); } @@ -10091,8 +10149,8 @@ public class WindowManagerService extends IWindowManager.Stub } mDisplayFrozen = true; - if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { - mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE; + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { + mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; mAppTransitionReady = true; } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 4db5239..82664eb 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -458,6 +458,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = new ArrayList<HistoryRecord>(); /** + * Animations that for the current transition have requested not to + * be considered for the transition animation. + */ + final ArrayList<HistoryRecord> mNoAnimActivities + = new ArrayList<HistoryRecord>(); + + /** * List of intents that were used to start the most recent tasks. */ final ArrayList<TaskRecord> mRecentTasks @@ -1572,6 +1579,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + long[] cpuSpeedTimes = mProcessStats.getLastCpuSpeedTimes(); final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics(); synchronized(bstats) { synchronized(mPidsSelfLocked) { @@ -1585,11 +1593,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (pr != null) { BatteryStatsImpl.Uid.Proc ps = pr.batteryStats; ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); + ps.addSpeedStepTimes(cpuSpeedTimes); } else { BatteryStatsImpl.Uid.Proc ps = bstats.getProcessStatsLocked(st.name, st.pid); if (ps != null) { ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); + ps.addSpeedStepTimes(cpuSpeedTimes); } } } @@ -2246,6 +2256,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen next.resumeKeyDispatchingLocked(); ensureActivitiesVisibleLocked(null, 0); mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); // Mark the point when the activity is resuming // TODO: To be more accurate, the mark should be before the onCreate, @@ -2562,6 +2573,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); return false; } @@ -2572,6 +2584,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); return false; } @@ -2634,17 +2647,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (prev.finishing) { if (DEBUG_TRANSITION) Log.v(TAG, "Prepare close transition: prev=" + prev); - mWindowManager.prepareAppTransition(prev.task == next.task - ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE - : WindowManagerPolicy.TRANSIT_TASK_CLOSE); + if (mNoAnimActivities.contains(prev)) { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + } else { + mWindowManager.prepareAppTransition(prev.task == next.task + ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE + : WindowManagerPolicy.TRANSIT_TASK_CLOSE); + } mWindowManager.setAppWillBeHidden(prev); mWindowManager.setAppVisibility(prev, false); } else { if (DEBUG_TRANSITION) Log.v(TAG, "Prepare open transition: prev=" + prev); - mWindowManager.prepareAppTransition(prev.task == next.task - ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN - : WindowManagerPolicy.TRANSIT_TASK_OPEN); + if (mNoAnimActivities.contains(next)) { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + } else { + mWindowManager.prepareAppTransition(prev.task == next.task + ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN + : WindowManagerPolicy.TRANSIT_TASK_OPEN); + } } if (false) { mWindowManager.setAppWillBeHidden(prev); @@ -2653,7 +2674,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } else if (mHistory.size() > 1) { if (DEBUG_TRANSITION) Log.v(TAG, "Prepare open transition: no previous"); - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN); + if (mNoAnimActivities.contains(next)) { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + } else { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN); + } } if (next.app != null && next.app.thread != null) { @@ -2696,6 +2721,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG); } mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); return true; } @@ -2856,9 +2882,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (DEBUG_TRANSITION) Log.v(TAG, "Prepare open transition: starting " + r); - mWindowManager.prepareAppTransition(newTask - ? WindowManagerPolicy.TRANSIT_TASK_OPEN - : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN); + if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + mNoAnimActivities.add(r); + } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_OPEN); + mNoAnimActivities.remove(r); + } else { + mWindowManager.prepareAppTransition(newTask + ? WindowManagerPolicy.TRANSIT_TASK_OPEN + : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN); + mNoAnimActivities.remove(r); + } mWindowManager.addAppToken( addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); boolean doShow = true; @@ -3333,7 +3368,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (callerAtFront) { // We really do want to push this one into the // user's face, right now. - moveTaskToFrontLocked(taskTop.task); + moveTaskToFrontLocked(taskTop.task, r); } } // If the caller has requested that the target task be @@ -6919,14 +6954,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen for (int i=0; i<N; i++) { TaskRecord tr = mRecentTasks.get(i); if (tr.taskId == task) { - moveTaskToFrontLocked(tr); + moveTaskToFrontLocked(tr, null); return; } } for (int i=mHistory.size()-1; i>=0; i--) { HistoryRecord hr = (HistoryRecord)mHistory.get(i); if (hr.task.taskId == task) { - moveTaskToFrontLocked(hr.task); + moveTaskToFrontLocked(hr.task, null); return; } } @@ -6936,7 +6971,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void moveTaskToFrontLocked(TaskRecord tr) { + private final void moveTaskToFrontLocked(TaskRecord tr, HistoryRecord reason) { if (DEBUG_SWITCH) Log.v(TAG, "moveTaskToFront: " + tr); final int task = tr.taskId; @@ -6947,10 +6982,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } - if (DEBUG_TRANSITION) Log.v(TAG, - "Prepare to front transition: task=" + tr); - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT); - ArrayList moved = new ArrayList(); // Applying the affinities may have removed entries from the history, @@ -6979,6 +7010,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen pos--; } + if (DEBUG_TRANSITION) Log.v(TAG, + "Prepare to front transition: task=" + tr); + if (reason != null && + (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + HistoryRecord r = topRunningActivityLocked(null); + if (r != null) { + mNoAnimActivities.add(r); + } + } else { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT); + } + mWindowManager.moveAppTokensToTop(moved); if (VALIDATE_TOKENS) { mWindowManager.validateAppTokens(mHistory); @@ -7004,7 +7048,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } final long origId = Binder.clearCallingIdentity(); - moveTaskToBackLocked(task); + moveTaskToBackLocked(task, null); Binder.restoreCallingIdentity(origId); } } @@ -7023,7 +7067,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); int taskId = getTaskForActivityLocked(token, !nonRoot); if (taskId >= 0) { - return moveTaskToBackLocked(taskId); + return moveTaskToBackLocked(taskId, null); } Binder.restoreCallingIdentity(origId); } @@ -7041,7 +7085,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * @param task The taskId to collect and move to the bottom. * @return Returns true if the move completed, false if not. */ - private final boolean moveTaskToBackLocked(int task) { + private final boolean moveTaskToBackLocked(int task, HistoryRecord reason) { Log.i(TAG, "moveTaskToBack: " + task); // If we have a watcher, preflight the move before committing to it. First check @@ -7092,6 +7136,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen pos++; } + if (reason != null && + (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + HistoryRecord r = topRunningActivityLocked(null); + if (r != null) { + mNoAnimActivities.add(r); + } + } else { + mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT); + } mWindowManager.moveAppTokensToBottom(moved); if (VALIDATE_TOKENS) { mWindowManager.validateAppTokens(mHistory); diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index ed0d534..61537f5 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -28,6 +28,7 @@ import android.util.Log; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.os.PowerProfile; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -49,6 +50,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub { public void publish(Context context) { mContext = context; ServiceManager.addService("batteryinfo", asBinder()); + mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps()); } public void shutdown() { diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 2672c6d..046bfea 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -111,6 +111,11 @@ public class PhoneNumberUtils return c == PAUSE || c == WAIT; } + /** Returns true if ch is not dialable or alpha char */ + private static boolean isSeparator(char ch) { + return !isDialable(ch) && !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')); + } + /** Extracts the phone number from an Intent. * * @param intent the intent to get the number of @@ -293,18 +298,36 @@ public class PhoneNumberUtils } /** + * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes. + */ + public static boolean compare(String a, String b) { + // We've used loose comparation at least Eclair, which may change in the future. + return compare(a, b, false); + } + + /** + * @hide only for testing. + */ + public static boolean compare(String a, String b, boolean useStrictComparation) { + return (useStrictComparation ? compareStrictly(a, b) : compareLoosely(a, b)); + } + + /** * Compare phone numbers a and b, return true if they're identical * enough for caller ID purposes. * * - Compares from right to left - * - requires MIN_MATCH (5) characters to match + * - requires MIN_MATCH (7) characters to match * - handles common trunk prefixes and international prefixes * (basically, everything except the Russian trunk prefix) * - * Tolerates nulls + * Note that this method does not return false even when the two phone numbers + * are not exactly same; rather; we can call this method "similar()", not "equals()". + * + * @hide */ public static boolean - compare(String a, String b) { + compareLoosely(String a, String b) { int ia, ib; int matched; @@ -391,6 +414,160 @@ public class PhoneNumberUtils } /** + * @hide + */ + public static boolean + compareStrictly(String a, String b) { + return compareStrictly(a, b, true); + } + + /** + * @hide + */ + public static boolean + compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix) { + if (a == null || b == null) { + return a == b; + } else if (a.length() == 0 && b.length() == 0) { + return false; + } + + int forwardIndexA = 0; + int forwardIndexB = 0; + + CountryCallingCodeAndNewIndex cccA = + tryGetCountryCallingCodeAndNewIndex(a, acceptInvalidCCCPrefix); + CountryCallingCodeAndNewIndex cccB = + tryGetCountryCallingCodeAndNewIndex(b, acceptInvalidCCCPrefix); + boolean bothHasCountryCallingCode = false; + boolean okToIgnorePrefix = true; + boolean trunkPrefixIsOmittedA = false; + boolean trunkPrefixIsOmittedB = false; + if (cccA != null && cccB != null) { + if (cccA.countryCallingCode != cccB.countryCallingCode) { + // Different Country Calling Code. Must be different phone number. + return false; + } + // When both have ccc, do not ignore trunk prefix. Without this, + // "+81123123" becomes same as "+810123123" (+81 == Japan) + okToIgnorePrefix = false; + bothHasCountryCallingCode = true; + forwardIndexA = cccA.newIndex; + forwardIndexB = cccB.newIndex; + } else if (cccA == null && cccB == null) { + // When both do not have ccc, do not ignore trunk prefix. Without this, + // "123123" becomes same as "0123123" + okToIgnorePrefix = false; + } else { + if (cccA != null) { + forwardIndexA = cccA.newIndex; + } else { + int tmp = tryGetTrunkPrefixOmittedIndex(b, 0); + if (tmp >= 0) { + forwardIndexA = tmp; + trunkPrefixIsOmittedA = true; + } + } + if (cccB != null) { + forwardIndexB = cccB.newIndex; + } else { + int tmp = tryGetTrunkPrefixOmittedIndex(b, 0); + if (tmp >= 0) { + forwardIndexB = tmp; + trunkPrefixIsOmittedB = true; + } + } + } + + int backwardIndexA = a.length() - 1; + int backwardIndexB = b.length() - 1; + while (backwardIndexA >= forwardIndexA && backwardIndexB >= forwardIndexB) { + boolean skip_compare = false; + final char chA = a.charAt(backwardIndexA); + final char chB = b.charAt(backwardIndexB); + if (isSeparator(chA)) { + backwardIndexA--; + skip_compare = true; + } + if (isSeparator(chB)) { + backwardIndexB--; + skip_compare = true; + } + + if (!skip_compare) { + if (chA != chB) { + return false; + } + backwardIndexA--; + backwardIndexB--; + } + } + + if (okToIgnorePrefix) { + if ((trunkPrefixIsOmittedA && forwardIndexA <= backwardIndexA) || + !checkPrefixIsIgnorable(a, forwardIndexA, backwardIndexA)) { + if (acceptInvalidCCCPrefix) { + // Maybe the code handling the special case for Thailand makes the + // result garbled, so disable the code and try again. + // e.g. "16610001234" must equal to "6610001234", but with + // Thailand-case handling code, they become equal to each other. + // + // Note: we select simplicity rather than adding some complicated + // logic here for performance(like "checking whether remaining + // numbers are just 66 or not"), assuming inputs are small + // enough. + return compare(a, b, false); + } else { + return false; + } + } + if ((trunkPrefixIsOmittedB && forwardIndexB <= backwardIndexB) || + !checkPrefixIsIgnorable(b, forwardIndexA, backwardIndexB)) { + if (acceptInvalidCCCPrefix) { + return compare(a, b, false); + } else { + return false; + } + } + } else { + // In the US, 1-650-555-1234 must be equal to 650-555-1234, + // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan. + // This request exists just in US (with 1 trunk (NDD) prefix). + // In addition, "011 11 7005554141" must not equal to "+17005554141", + // while "011 1 7005554141" must equal to "+17005554141" + // + // In this comparison, we ignore the prefix '1' just once, when + // - at least either does not have CCC, or + // - the remaining non-separator number is 1 + boolean maybeNamp = !bothHasCountryCallingCode; + while (backwardIndexA >= forwardIndexA) { + final char chA = a.charAt(backwardIndexA); + if (isDialable(chA)) { + if (maybeNamp && tryGetISODigit(chA) == 1) { + maybeNamp = false; + } else { + return false; + } + } + backwardIndexA--; + } + while (backwardIndexB >= forwardIndexB) { + final char chB = b.charAt(backwardIndexB); + if (isDialable(chB)) { + if (maybeNamp && tryGetISODigit(chB) == 1) { + maybeNamp = false; + } else { + return false; + } + } + backwardIndexB--; + } + } + + return true; + } + + /** * Returns the rightmost MIN_MATCH (5) characters in the network portion * in *reversed* order * @@ -475,54 +652,6 @@ public class PhoneNumberUtils } /** - * Phone numbers are stored in "lookup" form in the database - * as reversed strings to allow for caller ID lookup - * - * This method takes a phone number and makes a valid SQL "LIKE" - * string that will match the lookup form - * - */ - /** all of a up to len must be an international prefix or - * separators/non-dialing digits - */ - private static boolean - matchIntlPrefix(String a, int len) { - /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */ - /* 0 1 2 3 45 */ - - int state = 0; - for (int i = 0 ; i < len ; i++) { - char c = a.charAt(i); - - switch (state) { - case 0: - if (c == '+') state = 1; - else if (c == '0') state = 2; - else if (isNonSeparator(c)) return false; - break; - - case 2: - if (c == '0') state = 3; - else if (c == '1') state = 4; - else if (isNonSeparator(c)) return false; - break; - - case 4: - if (c == '1') state = 5; - else if (isNonSeparator(c)) return false; - break; - - default: - if (isNonSeparator(c)) return false; - break; - - } - } - - return state == 1 || state == 3 || state == 5; - } - - /** * 3GPP TS 24.008 10.5.4.7 * Called Party BCD Number * @@ -835,76 +964,6 @@ public class PhoneNumberUtils return result; } - /** all of 'a' up to len must match non-US trunk prefix ('0') */ - private static boolean - matchTrunkPrefix(String a, int len) { - boolean found; - - found = false; - - for (int i = 0 ; i < len ; i++) { - char c = a.charAt(i); - - if (c == '0' && !found) { - found = true; - } else if (isNonSeparator(c)) { - return false; - } - } - - return found; - } - - /** all of 'a' up to len must be a (+|00|011)country code) - * We're fast and loose with the country code. Any \d{1,3} matches */ - private static boolean - matchIntlPrefixAndCC(String a, int len) { - /* [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */ - /* 0 1 2 3 45 6 7 8 */ - - int state = 0; - for (int i = 0 ; i < len ; i++ ) { - char c = a.charAt(i); - - switch (state) { - case 0: - if (c == '+') state = 1; - else if (c == '0') state = 2; - else if (isNonSeparator(c)) return false; - break; - - case 2: - if (c == '0') state = 3; - else if (c == '1') state = 4; - else if (isNonSeparator(c)) return false; - break; - - case 4: - if (c == '1') state = 5; - else if (isNonSeparator(c)) return false; - break; - - case 1: - case 3: - case 5: - if (isISODigit(c)) state = 6; - else if (isNonSeparator(c)) return false; - break; - - case 6: - case 7: - if (isISODigit(c)) state++; - else if (isNonSeparator(c)) return false; - break; - - default: - if (isNonSeparator(c)) return false; - } - } - - return state == 6 || state == 7 || state == 8; - } - //================ Number formatting ========================= /** The current locale is unknown, look for a country code or don't format */ @@ -1143,10 +1202,24 @@ public class PhoneNumberUtils JapanesePhoneNumberFormatter.format(text); } - // Three and four digit phone numbers for either special services - // or from the network (eg carrier-originated SMS messages) should - // not match - static final int MIN_MATCH = 5; + // Three and four digit phone numbers for either special services, + // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should + // not match. + // + // This constant used to be 5, but SMS short codes has increased in length and + // can be easily 6 digits now days. Most countries have SMS short code length between + // 3 to 6 digits. The exceptions are + // + // Australia: Short codes are six or eight digits in length, starting with the prefix "19" + // followed by an additional four or six digits and two. + // Czech Republic: Codes are seven digits in length for MO and five (not billed) or + // eight (billed) for MT direction + // + // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference + // + // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match + // to 7. + static final int MIN_MATCH = 7; /** * isEmergencyNumber: checks a given number against the list of @@ -1553,4 +1626,317 @@ public class PhoneNumberUtils } return retStr; } + + //===== Begining of utility methods used in compareLoosely() ===== + + /** + * Phone numbers are stored in "lookup" form in the database + * as reversed strings to allow for caller ID lookup + * + * This method takes a phone number and makes a valid SQL "LIKE" + * string that will match the lookup form + * + */ + /** all of a up to len must be an international prefix or + * separators/non-dialing digits + */ + private static boolean + matchIntlPrefix(String a, int len) { + /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */ + /* 0 1 2 3 45 */ + + int state = 0; + for (int i = 0 ; i < len ; i++) { + char c = a.charAt(i); + + switch (state) { + case 0: + if (c == '+') state = 1; + else if (c == '0') state = 2; + else if (isNonSeparator(c)) return false; + break; + + case 2: + if (c == '0') state = 3; + else if (c == '1') state = 4; + else if (isNonSeparator(c)) return false; + break; + + case 4: + if (c == '1') state = 5; + else if (isNonSeparator(c)) return false; + break; + + default: + if (isNonSeparator(c)) return false; + break; + + } + } + + return state == 1 || state == 3 || state == 5; + } + + /** all of 'a' up to len must be a (+|00|011)country code) + * We're fast and loose with the country code. Any \d{1,3} matches */ + private static boolean + matchIntlPrefixAndCC(String a, int len) { + /* [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */ + /* 0 1 2 3 45 6 7 8 */ + + int state = 0; + for (int i = 0 ; i < len ; i++ ) { + char c = a.charAt(i); + + switch (state) { + case 0: + if (c == '+') state = 1; + else if (c == '0') state = 2; + else if (isNonSeparator(c)) return false; + break; + + case 2: + if (c == '0') state = 3; + else if (c == '1') state = 4; + else if (isNonSeparator(c)) return false; + break; + + case 4: + if (c == '1') state = 5; + else if (isNonSeparator(c)) return false; + break; + + case 1: + case 3: + case 5: + if (isISODigit(c)) state = 6; + else if (isNonSeparator(c)) return false; + break; + + case 6: + case 7: + if (isISODigit(c)) state++; + else if (isNonSeparator(c)) return false; + break; + + default: + if (isNonSeparator(c)) return false; + } + } + + return state == 6 || state == 7 || state == 8; + } + + /** all of 'a' up to len must match non-US trunk prefix ('0') */ + private static boolean + matchTrunkPrefix(String a, int len) { + boolean found; + + found = false; + + for (int i = 0 ; i < len ; i++) { + char c = a.charAt(i); + + if (c == '0' && !found) { + found = true; + } else if (isNonSeparator(c)) { + return false; + } + } + + return found; + } + + //===== End of utility methods used only in compareLoosely() ===== + + //===== Beggining of utility methods used only in compareStrictly() ==== + + /* + * If true, the number is country calling code. + */ + private static final boolean COUNTLY_CALLING_CALL[] = { + true, true, false, false, false, false, false, true, false, false, + false, false, false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, true, true, false, + true, true, true, true, true, false, true, false, false, true, + true, false, false, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, true, false, + true, true, true, true, true, true, true, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, true, true, true, true, false, true, false, false, true, + true, true, true, true, true, true, false, false, true, false, + }; + private static final int CCC_LENGTH = COUNTLY_CALLING_CALL.length; + + /** + * @return true when input is valid Country Calling Code. + */ + private static boolean isCountryCallingCode(int countryCallingCodeCandidate) { + return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH && + COUNTLY_CALLING_CALL[countryCallingCodeCandidate]; + } + + /** + * Returns interger corresponding to the input if input "ch" is + * ISO-LATIN characters 0-9. + * Returns -1 otherwise + */ + private static int tryGetISODigit(char ch) { + if ('0' <= ch && ch <= '9') { + return ch - '0'; + } else { + return -1; + } + } + + private static class CountryCallingCodeAndNewIndex { + public final int countryCallingCode; + public final int newIndex; + public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) { + this.countryCallingCode = countryCode; + this.newIndex = newIndex; + } + } + + /* + * Note that this function does not strictly care the country calling code with + * 3 length (like Morocco: +212), assuming it is enough to use the first two + * digit to compare two phone numbers. + */ + private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex( + String str, boolean acceptThailandCase) { + // Rough regexp: + // ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $ + // 0 1 2 3 45 6 7 89 + // + // In all the states, this function ignores separator characters. + // "166" is the special case for the call from Thailand to the US. Uguu! + int state = 0; + int ccc = 0; + final int length = str.length(); + for (int i = 0 ; i < length ; i++ ) { + char ch = str.charAt(i); + switch (state) { + case 0: + if (ch == '+') state = 1; + else if (ch == '0') state = 2; + else if (ch == '1') { + if (acceptThailandCase) { + state = 8; + } else { + return null; + } + } else if (isDialable(ch)) { + return null; + } + break; + + case 2: + if (ch == '0') state = 3; + else if (ch == '1') state = 4; + else if (isDialable(ch)) { + return null; + } + break; + + case 4: + if (ch == '1') state = 5; + else if (isDialable(ch)) { + return null; + } + break; + + case 1: + case 3: + case 5: + case 6: + case 7: + { + int ret = tryGetISODigit(ch); + if (ret > 0) { + ccc = ccc * 10 + ret; + if (ccc >= 100 || isCountryCallingCode(ccc)) { + return new CountryCallingCodeAndNewIndex(ccc, i + 1); + } + if (state == 1 || state == 3 || state == 5) { + state = 6; + } else { + state++; + } + } else if (isDialable(ch)) { + return null; + } + } + break; + case 8: + if (ch == '6') state = 9; + else if (isDialable(ch)) { + return null; + } + break; + case 9: + if (ch == '6') { + return new CountryCallingCodeAndNewIndex(66, i + 1); + } else { + return null; + } + default: + return null; + } + } + + return null; + } + + /** + * Currently this function simply ignore the first digit assuming it is + * trunk prefix. Actually trunk prefix is different in each country. + * + * e.g. + * "+79161234567" equals "89161234567" (Russian trunk digit is 8) + * "+33123456789" equals "0123456789" (French trunk digit is 0) + * + */ + private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) { + int length = str.length(); + for (int i = currentIndex ; i < length ; i++) { + final char ch = str.charAt(i); + if (tryGetISODigit(ch) >= 0) { + return i + 1; + } else if (isDialable(ch)) { + return -1; + } + } + return -1; + } + + /** + * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means + * that "str" has only one digit and separater characters. The one digit is + * assumed to be trunk prefix. + */ + private static boolean checkPrefixIsIgnorable(final String str, + int forwardIndex, int backwardIndex) { + boolean trunk_prefix_was_read = false; + while (backwardIndex >= forwardIndex) { + if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) { + if (trunk_prefix_was_read) { + // More than one digit appeared, meaning that "a" and "b" + // is different. + return false; + } else { + // Ignore just one digit, assuming it is trunk prefix. + trunk_prefix_was_read = true; + } + } else if (isDialable(str.charAt(backwardIndex))) { + // Trunk prefix is a digit, not "*", "#"... + return false; + } + backwardIndex--; + } + + return true; + } + + //==== End of utility methods used only in compareStrictly() ===== } diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java index 3edca66..52f25f6 100644 --- a/telephony/java/com/android/internal/telephony/BaseCommands.java +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -64,6 +64,7 @@ public abstract class BaseCommands implements CommandsInterface { protected RegistrantList mLineControlInfoRegistrants = new RegistrantList(); protected RegistrantList mT53ClirInfoRegistrants = new RegistrantList(); protected RegistrantList mT53AudCntrlInfoRegistrants = new RegistrantList(); + protected RegistrantList mRingbackToneRegistrants = new RegistrantList(); protected Registrant mSMSRegistrant; protected Registrant mNITZTimeRegistrant; @@ -569,6 +570,15 @@ public abstract class BaseCommands implements CommandsInterface { mT53AudCntrlInfoRegistrants.remove(h); } + public void registerForRingbackTone(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mRingbackToneRegistrants.add(r); + } + + public void unregisterForRingbackTone(Handler h) { + mRingbackToneRegistrants.remove(h); + } + //***** Protected Methods /** * Store new RadioState and send notification based on the changes diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java index 63bdc2c..9d83556 100644 --- a/telephony/java/com/android/internal/telephony/CommandsInterface.java +++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java @@ -525,6 +525,17 @@ public interface CommandsInterface { void registerForCdmaOtaProvision(Handler h,int what, Object obj); void unregisterForCdmaOtaProvision(Handler h); + /** + * Registers the handler when out-band ringback tone is needed.<p> + * + * Messages received from this: + * Message.obj will be an AsyncResult + * AsyncResult.userObj = obj + * AsyncResult.result = boolean. <p> + */ + void registerForRingbackTone(Handler h, int what, Object obj); + void unregisterForRingbackTone(Handler h); + /** * Supply the ICC PIN to the ICC card * diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java index e6fd0a0..a48900a 100644 --- a/telephony/java/com/android/internal/telephony/Connection.java +++ b/telephony/java/com/android/internal/telephony/Connection.java @@ -15,6 +15,7 @@ */ package com.android.internal.telephony; +import android.util.Log; /** * {@hide} @@ -27,6 +28,7 @@ public abstract class Connection { public static int PRESENTATION_UNKNOWN = 3; // no specified or unknown by network public static int PRESENTATION_PAYPHONE = 4; // show pay phone info + private static String LOG_TAG = "TelephonyConnection"; public enum DisconnectCause { NOT_DISCONNECTED, /* has not yet disconnected */ @@ -269,4 +271,25 @@ public abstract class Connection { */ public abstract int getNumberPresentation(); + /** + * Build a human representation of a connection instance, suitable for debugging. + * Don't log personal stuff unless in debug mode. + * @return a string representing the internal state of this connection. + */ + public String toString() { + StringBuilder str = new StringBuilder(128); + + if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { + str.append("addr: " + getAddress()) + .append(" pres.: " + getNumberPresentation()) + .append(" dial: " + getOrigDialString()) + .append(" postdial: " + getRemainingPostDialString()) + .append(" cnap name: " + getCnapName()) + .append("(" + getCnapNamePresentation() + ")"); + } + str.append(" incoming: " + isIncoming()) + .append(" state: " + getState()) + .append(" post dial state: " + getPostDialState()); + return str.toString(); + } } diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index f32837f..fffd128 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -379,6 +379,23 @@ public interface Phone { void unregisterForIncomingRing(Handler h); + /** + * Notifies when out-band ringback tone is needed.<p> + * + * Messages received from this: + * Message.obj will be an AsyncResult + * AsyncResult.userObj = obj + * AsyncResult.result = boolean, true to start play ringback tone + * and false to stop. <p> + */ + void registerForRingbackTone(Handler h, int what, Object obj); + + /** + * Unregisters for ringback tone notification. + */ + + void unregisterForRingbackTone(Handler h); + /** * Notifies when a voice connection has disconnected, either due to local diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index e340f85..1c62a82 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -482,6 +482,16 @@ public abstract class PhoneBase extends Handler implements Phone { mServiceStateRegistrants.remove(h); } + // Inherited documentation suffices. + public void registerForRingbackTone(Handler h, int what, Object obj) { + mCM.registerForRingbackTone(h,what,obj); + } + + // Inherited documentation suffices. + public void unregisterForRingbackTone(Handler h) { + mCM.unregisterForRingbackTone(h); + } + /** * Subclasses of Phone probably want to replace this with a * version scoped to their packages diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index 711a48c..c4f663a 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -331,6 +331,14 @@ public class PhoneProxy extends Handler implements Phone { mActivePhone.unregisterForEcmTimerReset(h); } + public void registerForRingbackTone(Handler h, int what, Object obj) { + mActivePhone.registerForRingbackTone(h,what,obj); + } + + public void unregisterForRingbackTone(Handler h) { + mActivePhone.unregisterForRingbackTone(h); + } + public boolean getIccRecordsLoaded() { return mActivePhone.getIccRecordsLoaded(); } diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index cd6340e..d1b446b 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -2313,6 +2313,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: ret = responseInts(p); break; case RIL_UNSOL_CDMA_INFO_REC: ret = responseCdmaInformationRecord(p); break; case RIL_UNSOL_OEM_HOOK_RAW: ret = responseRaw(p); break; + case RIL_UNSOL_RINGBACK_TONE: ret = responseInts(p); break; default: throw new RuntimeException("Unrecognized unsol response: " + response); @@ -2597,6 +2598,14 @@ public final class RIL extends BaseCommands implements CommandsInterface { mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, ret, null)); } break; + + case RIL_UNSOL_RINGBACK_TONE: + if (RILJ_LOGD) unsljLogvRet(response, ret); + if (mRingbackToneRegistrants != null) { + boolean playtone = (((int[])ret)[0] == 1); + mRingbackToneRegistrants.notifyRegistrants( + new AsyncResult (null, playtone, null)); + } } } @@ -3209,6 +3218,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: return "UNSOL_CDMA_OTA_PROVISION_STATUS"; case RIL_UNSOL_CDMA_INFO_REC: return "UNSOL_CDMA_INFO_REC"; case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW"; + case RIL_UNSOL_RINGBACK_TONE: return "UNSOL_RINGBACK_TONG"; default: return "<unknown reponse>"; } } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 90a82f9..3e9d8ad 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -258,4 +258,5 @@ cat include/telephony/ril.h | \ int RIL_UNSOL_CDMA_OTA_PROVISION_STATUS = 1026; int RIL_UNSOL_CDMA_INFO_REC = 1027; int RIL_UNSOL_OEM_HOOK_RAW = 1028; + int RIL_UNSOL_RINGBACK_TONE = 1029; } diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java index 3f0213b..3c2f2ed 100644 --- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java +++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java @@ -383,7 +383,7 @@ public abstract class SmsMessageBase { * 2. [x@y][ ]/[body] */ String[] parts = messageBody.split("( /)|( )", 2); - if (parts.length < 1) return; + if (parts.length < 2) return; emailFrom = parts[0]; emailBody = parts[1]; isEmail = true; diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java index 5bf1a0f..9fe2038 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java @@ -437,7 +437,7 @@ public final class CdmaCallTracker extends CallTracker { new AsyncResult(null, null, null)); } if (Phone.DEBUG_PHONE) { - log("update phone state, old= , new= , " + oldState + state); + log("update phone state, old=" + oldState + " new="+ state); } if (state != oldState) { phone.notifyPhoneStateChanged(); @@ -522,37 +522,44 @@ public final class CdmaCallTracker extends CallTracker { } } else { if (Phone.DEBUG_PHONE) { - log("pending Mo= , dc= " + pendingMO + dc); + log("pendingMo=" + pendingMO + ", dc=" + dc); } // find if the MT call is a new ring or unknown connection newRinging = checkMtFindNewRinging(dc,i); if (newRinging == null) { unknownConnectionAppeared = true; } + checkAndEnableDataCallAfterEmergencyCallDropped(); } hasNonHangupStateChanged = true; } else if (conn != null && dc == null) { + // This case means the RIL has no more active call anymore and + // we need to clean up the foregroundCall and ringingCall. + // Loop through foreground call connections as + // it contains the known logical connections. int count = foregroundCall.connections.size(); - if (count == 0) { - // Handle an unanswered MO/MT call, there is no - // foregroundCall connections at this time. - droppedDuringPoll.add(conn); - } else { - // Loop through foreground call connections as - // it contains the known logical connections. - for (int n = 0; n < count; n++) { - CdmaConnection cn = (CdmaConnection)foregroundCall.connections.get(n); - droppedDuringPoll.add(cn); - } + for (int n = 0; n < count; n++) { + if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll"); + CdmaConnection cn = (CdmaConnection)foregroundCall.connections.get(n); + droppedDuringPoll.add(cn); + } + count = ringingCall.connections.size(); + // Loop through ringing call connections as + // it may contain the known logical connections. + for (int n = 0; n < count; n++) { + if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll"); + CdmaConnection cn = (CdmaConnection)ringingCall.connections.get(n); + droppedDuringPoll.add(cn); } foregroundCall.setGeneric(false); + ringingCall.setGeneric(false); // Re-start Ecm timer when the connected emergency call ends if (mIsEcmTimerCanceled) { handleEcmTimer(phone.RESTART_ECM_TIMER); - } else { - mIsInEmergencyCall = false; } + // If emergency call is not going through while dialing + checkAndEnableDataCallAfterEmergencyCallDropped(); // Dropped connections are removed from the CallTracker // list but kept in the Call list @@ -568,6 +575,7 @@ public final class CdmaCallTracker extends CallTracker { if (newRinging == null) { unknownConnectionAppeared = true; } + checkAndEnableDataCallAfterEmergencyCallDropped(); } else { // Call info stored in conn is not consistent with the call info from dc. // We should follow the rule of MT calls taking precedence over MO calls @@ -1030,10 +1038,30 @@ public final class CdmaCallTracker extends CallTracker { */ private void disableDataCallInEmergencyCall(String dialString) { if (PhoneNumberUtils.isEmergencyNumber(dialString)) { + if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall"); phone.disableDataConnectivity(); mIsInEmergencyCall = true; } } + + /** + * Check and enable data call after an emergency call is dropped if it's + * not in ECM + */ + private void checkAndEnableDataCallAfterEmergencyCallDropped() { + if (mIsInEmergencyCall) { + String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); + if (Phone.DEBUG_PHONE) { + log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm); + } + if (inEcm.compareTo("false") == 0) { + // Re-initiate data connection + phone.mDataConnection.setDataEnabled(true); + } + mIsInEmergencyCall = false; + } + } + /** * Check the MT call to see if it's a new ring or * a unknown connection. diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java index 0c94e6a..bc04e02 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java @@ -221,10 +221,6 @@ public class CdmaConnection extends Connection { return isIncoming == c.isMT && equalsHandlesNulls(address, cAddress); } - public String - toString() { - return (isIncoming ? "incoming" : "outgoing"); - } public String getOrigDialString(){ return dialString; @@ -492,6 +488,8 @@ public class CdmaConnection extends Connection { newParent = parentFromDCState(dc.state); + if (Phone.DEBUG_PHONE) log("parent= " +parent +", newParent= " + newParent); + if (!equalsHandlesNulls(address, dc.number)) { if (Phone.DEBUG_PHONE) log("update: phone # changed!"); address = dc.number; @@ -509,7 +507,7 @@ public class CdmaConnection extends Connection { cnapName = dc.name; } - log("--dssds----"+cnapName); + if (Phone.DEBUG_PHONE) log("--dssds----"+cnapName); cnapNamePresentation = dc.namePresentation; numberPresentation = dc.numberPresentation; @@ -529,9 +527,7 @@ public class CdmaConnection extends Connection { /** Some state-transition events */ if (Phone.DEBUG_PHONE) log( - "update: parent=" + parent + - ", hasNewParent=" + (newParent != parent) + - ", wasConnectingInOrOut=" + wasConnectingInOrOut + + "Update, wasConnectingInOrOut=" + wasConnectingInOrOut + ", wasHolding=" + wasHolding + ", isConnectingInOrOut=" + isConnectingInOrOut() + ", changed=" + changed); @@ -860,10 +856,13 @@ public class CdmaConnection extends Connection { // Append the PW char ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT; - // if there is a PAUSE in at the beginning of PW character sequences, and this - // PW character sequences has more than 2 PAUSE and WAIT Characters,skip PAUSE, - // append WAIT. - if (isPause(c) && (nextNonPwCharIndex > (currPwIndex + 2))) { + // If the nextNonPwCharIndex is greater than currPwIndex + 1, + // it means the PW sequence contains not only P characters. + // Since for the sequence that only contains P character, + // the P character is handled one by one, the nextNonPwCharIndex + // equals to currPwIndex + 1. + // In this case, skip P, append W. + if (nextNonPwCharIndex > (currPwIndex + 1)) { ret = PhoneNumberUtils.WAIT; } return ret; @@ -882,6 +881,11 @@ public class CdmaConnection extends Connection { * and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT. */ public static String formatDialString(String phoneNumber) { + /** + * TODO(cleanup): This function should move to PhoneNumberUtils, and + * tests should be added. + */ + if (phoneNumber == null) { return null; } @@ -901,9 +905,9 @@ public class CdmaConnection extends Connection { char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex); ret.append(pC); // If PW char sequence has more than 2 PW characters, - // skip to the last character since the sequence already be + // skip to the last PW character since the sequence already be // converted to WAIT character - if (nextIndex > (currIndex + 2)) { + if (nextIndex > (currIndex + 1)) { currIndex = nextIndex - 1; } } else if (nextIndex == length) { diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index 9ac78eb..a410f0e 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -538,12 +538,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { if (!mPendingRadioPowerOffAfterDataOff) { DataConnectionTracker.State currentState = dcTracker.getState(); if (currentState != DataConnectionTracker.State.CONNECTED - && currentState != DataConnectionTracker.State.DISCONNECTING) { + && currentState != DataConnectionTracker.State.DISCONNECTING + && currentState != DataConnectionTracker.State.INITING) { if (DBG) log("Data disconnected, turn off radio right away."); cm.setRadioPower(false, null); } - else if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 5000)) { - if (DBG) log("Wait 5 sec for data to be disconnected, then turn off radio."); + else if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) { + if (DBG) { + log("Wait up to 30 sec for data to disconnect, then turn off radio."); + } mPendingRadioPowerOffAfterDataOff = true; } else { Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away."); diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java index 2091fb6..445be39 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java @@ -180,11 +180,6 @@ public class GsmConnection extends Connection { return isIncoming == c.isMT && equalsHandlesNulls(address, cAddress); } - public String - toString() { - return (isIncoming ? "incoming" : "outgoing"); - } - public String getAddress() { return address; } diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java index 5368526..57b22f8 100644 --- a/test-runner/android/test/mock/MockContext.java +++ b/test-runner/android/test/mock/MockContext.java @@ -264,6 +264,13 @@ public class MockContext extends Context { } @Override + public void sendStickyOrderedBroadcast(Intent intent, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { + throw new UnsupportedOperationException(); + } + + @Override public void removeStickyBroadcast(Intent intent) { throw new UnsupportedOperationException(); } diff --git a/tests/AndroidTests/Android.mk b/tests/AndroidTests/Android.mk index f5e49d7..ced796a 100644 --- a/tests/AndroidTests/Android.mk +++ b/tests/AndroidTests/Android.mk @@ -8,7 +8,7 @@ LOCAL_JAVA_LIBRARIES := framework-tests android.test.runner LOCAL_STATIC_JAVA_LIBRARIES := googlelogin-client # Resource unit tests use a private locale -LOCAL_AAPT_FLAGS = -c xx_YY -c cs +LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c 160dpi -c 32dpi -c 240dpi LOCAL_SRC_FILES := \ $(call all-subdir-java-files) \ diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java index 0991e8c..853f10a 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java @@ -204,16 +204,18 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase assertEquals("+" + PHONE_NUMBER, number); c.close(); } - - - private void phoneNumberCompare(String phone1, String phone2, boolean equal) - throws Exception { + + private void phoneNumberCompare(String phone1, String phone2, boolean equal, + boolean useStrictComparation) { String[] temporalPhoneNumbers = new String[2]; temporalPhoneNumbers[0] = phone1; temporalPhoneNumbers[1] = phone2; Cursor cursor = mDatabase.rawQuery( - "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END", + String.format( + "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?, %d) " + + "THEN 'equal' ELSE 'not equal' END", + (useStrictComparation ? 1 : 0)), temporalPhoneNumbers); try { assertNotNull(cursor); @@ -233,11 +235,23 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase } private void assertPhoneNumberEqual(String phone1, String phone2) throws Exception { - phoneNumberCompare(phone1, phone2, true); + assertPhoneNumberEqual(phone1, phone2, true); + assertPhoneNumberEqual(phone1, phone2, false); + } + + private void assertPhoneNumberEqual(String phone1, String phone2, boolean useStrict) + throws Exception { + phoneNumberCompare(phone1, phone2, true, useStrict); } private void assertPhoneNumberNotEqual(String phone1, String phone2) throws Exception { - phoneNumberCompare(phone1, phone2, false); + assertPhoneNumberNotEqual(phone1, phone2, true); + assertPhoneNumberNotEqual(phone1, phone2, false); + } + + private void assertPhoneNumberNotEqual(String phone1, String phone2, boolean useStrict) + throws Exception { + phoneNumberCompare(phone1, phone2, false, useStrict); } /** @@ -252,7 +266,8 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase assertPhoneNumberNotEqual("123123", "923123"); assertPhoneNumberNotEqual("123123", "123129"); assertPhoneNumberNotEqual("123123", "1231234"); - assertPhoneNumberNotEqual("123123", "0123123"); + assertPhoneNumberEqual("123123", "0123123", false); + assertPhoneNumberNotEqual("123123", "0123123", true); assertPhoneNumberEqual("650-253-0000", "6502530000"); assertPhoneNumberEqual("650-253-0000", "650 253 0000"); assertPhoneNumberEqual("650 253 0000", "6502530000"); @@ -291,11 +306,13 @@ public class DatabaseGeneralTest extends TestCase implements PerformanceTestCase assertPhoneNumberEqual("+593-2-1234-123", "21234123"); // Two continuous 0 at the beginning of the phone string should not be - // treated as trunk prefix. - assertPhoneNumberNotEqual("008001231234", "8001231234"); + // treated as trunk prefix in the strict comparation. + assertPhoneNumberEqual("008001231234", "8001231234", false); + assertPhoneNumberNotEqual("008001231234", "8001231234", true); - // Confirm that the bug found before does not re-appear. - assertPhoneNumberNotEqual("080-1234-5678", "+819012345678"); + // Confirm that the bug found before does not re-appear in the strict compalation + assertPhoneNumberEqual("080-1234-5678", "+819012345678", false); + assertPhoneNumberNotEqual("080-1234-5678", "+819012345678", true); } @MediumTest diff --git a/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerMeasurement.java b/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerMeasurement.java index 74ac865..861e37b 100644 --- a/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerMeasurement.java +++ b/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerMeasurement.java @@ -12,7 +12,8 @@ public class PowerMeasurement extends ActivityInstrumentationTestCase2<PowerTest private static final String LOGTAG = "PowerMeasurement"; private static final String PKG_NAME = "com.android.browserpowertest"; - private static final String TESTING_URL = "http://www.espn.com"; + private static final String TESTING_URL = + "http://75.17.48.204:10088/nyt/index.html"; private static final int TIME_OUT = 2 * 60 * 1000; private static final int DELAY = 0; @@ -46,6 +47,24 @@ public class PowerMeasurement extends ActivityInstrumentationTestCase2<PowerTest pageErrorFlag); Log.v(LOGTAG, "Page is loaded in " + activity.getPageLoadTime() + " ms."); + // Force to clean up the cache dir so that it get back to the clean + // state + Runtime fileRemoval = Runtime.getRuntime(); + String cmdBecomeSu = "su"; + boolean clearCacheSuccess = false; + try{ + Process runsum = fileRemoval.exec(cmdBecomeSu); + int exitVal = runsum.waitFor(); + String rmfile = "rm -r /data/data/com.android.browserpowertest/cache"; + Process removal = fileRemoval.exec(rmfile); + exitVal = removal.waitFor(); + if (exitVal == 0) { + clearCacheSuccess = true; + } + } catch ( Exception e){ + assertTrue("Fails to clear the cahche", false); + } + assertTrue("Fails to clear the cahche", clearCacheSuccess); activity.finish(); } } diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java index 7426d33..20ea4d7 100644 --- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java +++ b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java @@ -257,19 +257,19 @@ public class PhoneNumberUtilsTest extends TestCase { @SmallTest public void testToCallerIDIndexable() throws Exception { - assertEquals("14145", PhoneNumberUtils.toCallerIDMinMatch("17005554141")); - assertEquals("14145", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141")); - assertEquals("14145", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141,1234")); - assertEquals("14145", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141;1234")); + assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("17005554141")); + assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141")); + assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141,1234")); + assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141;1234")); //this seems wrong, or at least useless - assertEquals("NN145", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-41NN")); + assertEquals("NN14555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-41NN")); //<shrug> -- these are all not useful, but not terribly wrong assertEquals("", PhoneNumberUtils.toCallerIDMinMatch("")); assertEquals("0032", PhoneNumberUtils.toCallerIDMinMatch("2300")); assertEquals("0032+", PhoneNumberUtils.toCallerIDMinMatch("+2300")); - assertEquals("#130#", PhoneNumberUtils.toCallerIDMinMatch("*#031#")); + assertEquals("#130#*", PhoneNumberUtils.toCallerIDMinMatch("*#031#")); } @SmallTest diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index f2cdf75..4742341 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -712,7 +712,8 @@ int doDump(Bundle* bundle) LARGE_SCREEN_ATTR, NULL, 1); } else if (tag == "uses-feature") { String8 name = getAttribute(tree, NAME_ATTR, &error); - if (error == "") { + + if (name != "" && error == "") { int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); if (name == "android.hardware.camera") { @@ -729,7 +730,7 @@ int doDump(Bundle* bundle) } } else if (tag == "uses-permission") { String8 name = getAttribute(tree, NAME_ATTR, &error); - if (error == "") { + if (name != "" && error == "") { if (name == "android.permission.CAMERA") { hasCameraPermission = true; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index 4e7e925..1e9f573 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -1099,6 +1099,13 @@ public final class BridgeContext extends Context { } @Override + public void sendStickyOrderedBroadcast(Intent intent, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { + // TODO Auto-generated method stub + } + + @Override public void setTheme(int arg0) { // TODO Auto-generated method stub diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 954930e..01bc919 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -71,7 +71,7 @@ public class WifiConfiguration implements Parcelable { /** {@hide} */ public EnterpriseField phase2 = new EnterpriseField("phase2"); /** {@hide} */ - public EnterpriseField identity = new EnterpriseField("anonymous_identity"); + public EnterpriseField identity = new EnterpriseField("identity"); /** {@hide} */ public EnterpriseField anonymous_identity = new EnterpriseField("anonymous_identity"); /** {@hide} */ diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index e3d8bf4..5638480 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -907,6 +907,7 @@ public class WifiStateTracker extends NetworkStateTracker { } } } else if (newState == SupplicantState.DISCONNECTED) { + mHaveIpAddress = false; if (isDriverStopped() || mDisconnectExpected) { handleDisconnectedState(DetailedState.DISCONNECTED); } else { @@ -1007,20 +1008,17 @@ public class WifiStateTracker extends NetworkStateTracker { setNotificationVisible(false, 0, false, 0); boolean wasDisconnectPending = mDisconnectPending; cancelDisconnect(); - if (!TextUtils.equals(mWifiInfo.getSSID(), mLastSsid)) { - /* - * The connection is fully configured as far as link-level - * connectivity is concerned, but we may still need to obtain - * an IP address. But do this only if we are connecting to - * a different network than we were connected to previously. - */ - if (wasDisconnectPending) { - DetailedState saveState = getNetworkInfo().getDetailedState(); - handleDisconnectedState(DetailedState.DISCONNECTED); - setDetailedStateInternal(saveState); - } - configureInterface(); + /* + * The connection is fully configured as far as link-level + * connectivity is concerned, but we may still need to obtain + * an IP address. + */ + if (wasDisconnectPending) { + DetailedState saveState = getNetworkInfo().getDetailedState(); + handleDisconnectedState(DetailedState.DISCONNECTED); + setDetailedStateInternal(saveState); } + configureInterface(); mLastBssid = result.BSSID; mLastSsid = mWifiInfo.getSSID(); mLastNetworkId = result.networkId; |