Merge "Add ability to parse HTTP-format moratorium times (since pretty much every user wants this)."
diff --git a/api/current.xml b/api/current.xml
index 5e34322..0893e4e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -24873,17 +24873,6 @@
visibility="public"
>
</field>
-<field name="INTENT_ACTION_SELECT_SEARCH_SOURCE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value=""android.intent.action.SELECT_SEARCH_SOURCE""
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="INTENT_ACTION_WEB_SEARCH_SETTINGS"
type="java.lang.String"
transient="false"
@@ -55617,7 +55606,7 @@
>
</field>
</class>
-<class name="GestureUtilities"
+<class name="GestureUtils"
extends="java.lang.Object"
abstract="false"
static="false"
@@ -80725,6 +80714,21 @@
<parameter name="tag" type="java.lang.String">
</parameter>
</method>
+<method name="getAttributeDouble"
+ return="double"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+<parameter name="defaultValue" type="double">
+</parameter>
+</method>
<method name="getAttributeInt"
return="int"
abstract="false"
@@ -80924,6 +80928,17 @@
visibility="public"
>
</field>
+<field name="TAG_FOCAL_LENGTH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""FocalLength""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TAG_GPS_DATESTAMP"
type="java.lang.String"
transient="false"
@@ -86759,7 +86774,7 @@
visibility="public"
>
</method>
-<method name="getMobileRxPkts"
+<method name="getMobileRxPackets"
return="long"
abstract="false"
native="false"
@@ -86781,7 +86796,7 @@
visibility="public"
>
</method>
-<method name="getMobileTxPkts"
+<method name="getMobileTxPackets"
return="long"
abstract="false"
native="false"
@@ -86803,7 +86818,7 @@
visibility="public"
>
</method>
-<method name="getTotalRxPkts"
+<method name="getTotalRxPackets"
return="long"
abstract="false"
native="false"
@@ -86825,7 +86840,7 @@
visibility="public"
>
</method>
-<method name="getTotalTxPkts"
+<method name="getTotalTxPackets"
return="long"
abstract="false"
native="false"
@@ -159338,6 +159353,21 @@
</parameter>
</method>
<method name="tokenize"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="out" type="java.util.Collection<android.text.util.Rfc822Token>">
+</parameter>
+</method>
+<method name="tokenize"
return="android.text.util.Rfc822Token[]"
abstract="false"
native="false"
@@ -186327,6 +186357,108 @@
</parameter>
</method>
</class>
+<class name="ConsoleMessage"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ConsoleMessage"
+ type="android.webkit.ConsoleMessage"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="sourceId" type="java.lang.String">
+</parameter>
+<parameter name="lineNumber" type="int">
+</parameter>
+<parameter name="msgLevel" type="android.webkit.ConsoleMessage.MessageLevel">
+</parameter>
+</constructor>
+<method name="lineNumber"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="message"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="messageLevel"
+ return="android.webkit.ConsoleMessage.MessageLevel"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="sourceId"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="ConsoleMessage.MessageLevel"
+ extends="java.lang.Enum"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="valueOf"
+ return="android.webkit.ConsoleMessage.MessageLevel"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="values"
+ return="android.webkit.ConsoleMessage.MessageLevel[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
<class name="CookieManager"
extends="java.lang.Object"
abstract="false"
@@ -187815,7 +187947,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="message" type="java.lang.String">
@@ -187825,6 +187957,19 @@
<parameter name="sourceID" type="java.lang.String">
</parameter>
</method>
+<method name="onConsoleMessage"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="consoleMessage" type="android.webkit.ConsoleMessage">
+</parameter>
+</method>
<method name="onCreateWindow"
return="boolean"
abstract="false"
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 8c15d0b..acfbb07 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -315,7 +315,7 @@
for (RestoreSet s : sets) {
if (s.token == token) {
System.out.println("Scheduling restore: " + s.name);
- didRestore = (mRestore.performRestore(token, observer) == 0);
+ didRestore = (mRestore.restoreAll(token, observer) == 0);
break;
}
}
diff --git a/common/java/com/android/common/Base64.java b/common/java/com/android/common/Base64.java
new file mode 100644
index 0000000..d108f44
--- /dev/null
+++ b/common/java/com/android/common/Base64.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.common;
+
+/**
+ * Utilities for encoding and decoding the Base64 encoding. See RFCs
+ * 2045 and 3548.
+ */
+public class Base64 {
+ /**
+ * Encoder flag bit to indicate you want the padding '='
+ * characters at the end (if any) to be omitted.
+ */
+ public static final int NO_PADDING = 1;
+
+ /**
+ * Encoder flag bit to indicate you want all line terminators to
+ * be omitted (ie, the output will be on one long line).
+ */
+ public static final int NO_WRAP = 2;
+
+ /**
+ * Encoder flag bit to indicate you want lines to be ended with
+ * CRLF instead of just LF.
+ */
+ public static final int CRLF = 4;
+
+ /**
+ * Encoder/decoder flag bit to indicate using the "web safe"
+ * variant of Base64 (see RFC 3548 section 4) where '-' and '_'
+ * are used in place of '+' and '/'.
+ */
+ public static final int WEB_SAFE = 8;
+
+ /**
+ * Lookup table for turning bytes into their position in the
+ * Base64 alphabet.
+ */
+ private static final int DECODE[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ /**
+ * Decode lookup table for the "web safe" variant (RFC 3548
+ * sec. 4) where - and _ replace + and /.
+ */
+ private static final int DECODE_WEBSAFE[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ /** Non-data values in the DECODE arrays. */
+ private static final int SKIP = -1;
+ private static final int EQUALS = -2;
+
+ /**
+ * Decode the Base64-encoded data in input and return the data in
+ * a new byte array.
+ *
+ * The padding '=' characters at the end are considered optional, but
+ * if any are present, there must be the correct number of them.
+ *
+ * @param input the input String to decode, which is converted to
+ * bytes using the default charset
+ * @param flags controls certain features of the decoded output.
+ * Passing 0 to decode standard Base64.
+ *
+ * @throws IllegalArgumentException if the input contains
+ * incorrect padding
+ */
+ public static byte[] decode(String str, int flags) {
+ return decode(str.getBytes(), flags);
+ }
+
+ /**
+ * Decode the Base64-encoded data in input and return the data in
+ * a new byte array.
+ *
+ * The padding '=' characters at the end are considered optional, but
+ * if any are present, there must be the correct number of them.
+ *
+ * @param input the input array to decode
+ * @param flags controls certain features of the decoded output.
+ * Passing 0 to decode standard Base64.
+ *
+ * @throws IllegalArgumentException if the input contains
+ * incorrect padding
+ */
+ public static byte[] decode(byte[] input, int flags) {
+ return decode(input, 0, input.length, flags);
+ }
+
+ /**
+ * Decode the Base64-encoded data in input and return the data in
+ * a new byte array.
+ *
+ * The padding '=' characters at the end are considered optional, but
+ * if any are present, there must be the correct number of them.
+ *
+ * @param input the data to decode
+ * @param offset the position within the input array at which to start
+ * @param len the number of bytes of input to decode
+ * @param flags controls certain features of the decoded output.
+ * Passing 0 to decode standard Base64.
+ *
+ * @throws IllegalArgumentException if the input contains
+ * incorrect padding
+ */
+ public static byte[] decode(byte[] input, int offset, int len, int flags) {
+ int p = offset;
+ // Allocate space for the most data the input could represent.
+ // (It could contain less if it contains whitespace, etc.)
+ byte[] output = new byte[len*3/4];
+ len += offset;
+ int op = 0;
+
+ final int[] decode = ((flags & WEB_SAFE) == 0) ?
+ DECODE : DECODE_WEBSAFE;
+
+ int state = 0;
+ int value = 0;
+
+ while (p < len) {
+
+ // Try the fast path: we're starting a new tuple and the
+ // next four bytes of the input stream are all data
+ // bytes. This corresponds to going through states
+ // 0-1-2-3-0. We expect to use this method for most of
+ // the data.
+ //
+ // If any of the next four bytes of input are non-data
+ // (whitespace, etc.), value will end up negative. (All
+ // the non-data values in decode are small negative
+ // numbers, so shifting any of them up and or'ing them
+ // together will result in a value with its top bit set.)
+ //
+ // You can remove this whole block and the output should
+ // be the same, just slower.
+ if (state == 0 && p+4 <= len &&
+ (value = ((decode[input[p] & 0xff] << 18) |
+ (decode[input[p+1] & 0xff] << 12) |
+ (decode[input[p+2] & 0xff] << 6) |
+ (decode[input[p+3] & 0xff]))) >= 0) {
+ output[op+2] = (byte) value;
+ output[op+1] = (byte) (value >> 8);
+ output[op] = (byte) (value >> 16);
+ op += 3;
+ p += 4;
+ continue;
+ }
+
+ // The fast path isn't available -- either we've read a
+ // partial tuple, or the next four input bytes aren't all
+ // data, or whatever. Fall back to the slower state
+ // machine implementation.
+ //
+ // States 0-3 are reading through the next input tuple.
+ // State 4 is having read one '=' and expecting exactly
+ // one more.
+ // State 5 is expecting no more data or padding characters
+ // in the input.
+
+ int d = decode[input[p++] & 0xff];
+
+ switch (state) {
+ case 0:
+ if (d >= 0) {
+ value = d;
+ ++state;
+ } else if (d != SKIP) {
+ throw new IllegalArgumentException("bad base-64");
+ }
+ break;
+
+ case 1:
+ if (d >= 0) {
+ value = (value << 6) | d;
+ ++state;
+ } else if (d != SKIP) {
+ throw new IllegalArgumentException("bad base-64");
+ }
+ break;
+
+ case 2:
+ if (d >= 0) {
+ value = (value << 6) | d;
+ ++state;
+ } else if (d == EQUALS) {
+ // Emit the last (partial) output tuple;
+ // expect exactly one more padding character.
+ output[op++] = (byte) (value >> 4);
+ state = 4;
+ } else if (d != SKIP) {
+ throw new IllegalArgumentException("bad base-64");
+ }
+ break;
+
+ case 3:
+ if (d >= 0) {
+ // Emit the output triple and return to state 0.
+ value = (value << 6) | d;
+ output[op+2] = (byte) value;
+ output[op+1] = (byte) (value >> 8);
+ output[op] = (byte) (value >> 16);
+ op += 3;
+ state = 0;
+ } else if (d == EQUALS) {
+ // Emit the last (partial) output tuple;
+ // expect no further data or padding characters.
+ output[op+1] = (byte) (value >> 2);
+ output[op] = (byte) (value >> 10);
+ op += 2;
+ state = 5;
+ } else if (d != SKIP) {
+ throw new IllegalArgumentException("bad base-64");
+ }
+ break;
+
+ case 4:
+ if (d == EQUALS) {
+ ++state;
+ } else if (d != SKIP) {
+ throw new IllegalArgumentException("bad base-64");
+ }
+ break;
+
+ case 5:
+ if (d != SKIP) {
+ throw new IllegalArgumentException("bad base-64");
+ }
+ break;
+ }
+ }
+
+ // Done reading input. Now figure out where we are left in
+ // the state machine and finish up.
+
+ switch (state) {
+ case 0:
+ // Output length is a multiple of three. Fine.
+ break;
+ case 1:
+ // Read one extra input byte, which isn't enough to
+ // make another output byte. Illegal.
+ throw new IllegalArgumentException("bad base-64");
+ case 2:
+ // Read two extra input bytes, enough to emit 1 more
+ // output byte. Fine.
+ output[op++] = (byte) (value >> 4);
+ break;
+ case 3:
+ // Read three extra input bytes, enough to emit 2 more
+ // output bytes. Fine.
+ output[op+1] = (byte) (value >> 2);
+ output[op] = (byte) (value >> 10);
+ op += 2;
+ break;
+ case 4:
+ // Read one padding '=' when we expected 2. Illegal.
+ throw new IllegalArgumentException("bad base-64");
+ case 5:
+ // Read all the padding '='s we expected and no more.
+ // Fine.
+ break;
+ }
+
+ // Maybe we got lucky and allocated exactly enough output space.
+ if (op == output.length) {
+ return output;
+ }
+
+ // Need to shorten the array, so allocate a new one of the
+ // right size and copy.
+ byte[] temp = new byte[op];
+ System.arraycopy(output, 0, temp, 0, op);
+ return temp;
+ }
+
+ /**
+ * Emit a new line every this many output tuples. Corresponds to
+ * a 76-character line length (the maximum allowable according to
+ * RFC 2045).
+ */
+ private static final int LINE_GROUPS = 19;
+
+ /**
+ * Lookup table for turning Base64 alphabet positions (6 bits)
+ * into output bytes.
+ */
+ private static final byte ENCODE[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/',
+ };
+
+ /**
+ * Lookup table for turning Base64 alphabet positions (6 bits)
+ * into output bytes.
+ */
+ private static final byte ENCODE_WEBSAFE[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '-', '_',
+ };
+
+ /**
+ * Base64-encode the given data and return a newly allocated
+ * String with the result.
+ *
+ * @param input the data to encode
+ * @param flags controls certain features of the encoded output.
+ * Passing 0 results in output that adheres to RFC
+ * 2045.
+ */
+ public static String encodeString(byte[] input, int flags) {
+ return new String(encode(input, flags));
+ }
+
+ /**
+ * Base64-encode the given data and return a newly allocated
+ * String with the result.
+ *
+ * @param input the data to encode
+ * @param offset the position within the input array at which to
+ * start
+ * @param len the number of bytes of input to encode
+ * @param flags controls certain features of the encoded output.
+ * Passing 0 results in output that adheres to RFC
+ * 2045.
+ */
+ public static String encodeString(byte[] input, int offset, int len, int flags) {
+ return new String(encode(input, offset, len, flags));
+ }
+
+ /**
+ * Base64-encode the given data and return a newly allocated
+ * byte[] with the result.
+ *
+ * @param input the data to encode
+ * @param flags controls certain features of the encoded output.
+ * Passing 0 results in output that adheres to RFC
+ * 2045.
+ */
+ public static byte[] encode(byte[] input, int flags) {
+ return encode(input, 0, input.length, flags);
+ }
+
+ /**
+ * Base64-encode the given data and return a newly allocated
+ * byte[] with the result.
+ *
+ * @param input the data to encode
+ * @param offset the position within the input array at which to
+ * start
+ * @param len the number of bytes of input to encode
+ * @param flags controls certain features of the encoded output.
+ * Passing 0 results in output that adheres to RFC
+ * 2045.
+ */
+ public static byte[] encode(byte[] input, int offset, int len, int flags) {
+ final boolean do_padding = (flags & NO_PADDING) == 0;
+ final boolean do_newline = (flags & NO_WRAP) == 0;
+ final boolean do_cr = (flags & CRLF) != 0;
+
+ final byte[] encode = ((flags & WEB_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE;
+
+ // Compute the exact length of the array we will produce.
+ int output_len = len / 3 * 4;
+
+ // Account for the tail of the data and the padding bytes, if any.
+ if (do_padding) {
+ if (len % 3 > 0) {
+ output_len += 4;
+ }
+ } else {
+ switch (len % 3) {
+ case 0: break;
+ case 1: output_len += 2; break;
+ case 2: output_len += 3; break;
+ }
+ }
+
+ // Account for the newlines, if any.
+ if (do_newline && len > 0) {
+ output_len += (((len-1) / (3 * LINE_GROUPS)) + 1) * (do_cr ? 2 : 1);
+ }
+
+ int op = 0;
+ byte[] output = new byte[output_len];
+
+ // The main loop, turning 3 input bytes into 4 output bytes on
+ // each iteration.
+ int count = do_newline ? LINE_GROUPS : -1;
+ int p = offset;
+ len += offset;
+ while (p+3 <= len) {
+ int v = ((input[p++] & 0xff) << 16) |
+ ((input[p++] & 0xff) << 8) |
+ (input[p++] & 0xff);
+ output[op++] = encode[(v >> 18) & 0x3f];
+ output[op++] = encode[(v >> 12) & 0x3f];
+ output[op++] = encode[(v >> 6) & 0x3f];
+ output[op++] = encode[v & 0x3f];
+ if (--count == 0) {
+ if (do_cr) output[op++] = '\r';
+ output[op++] = '\n';
+ count = LINE_GROUPS;
+ }
+ }
+
+ // Finish up the tail of the input.
+ if (p == len-1) {
+ int v = (input[p] & 0xff) << 4;
+ output[op++] = encode[(v >> 6) & 0x3f];
+ output[op++] = encode[v & 0x3f];
+ if (do_padding) {
+ output[op++] = '=';
+ output[op++] = '=';
+ }
+ if (do_newline) {
+ if (do_cr) output[op++] = '\r';
+ output[op++] = '\n';
+ }
+ } else if (p == len-2) {
+ int v = ((input[p] & 0xff) << 10) | ((input[p+1] & 0xff) << 2);
+ output[op++] = encode[(v >> 12) & 0x3f];
+ output[op++] = encode[(v >> 6) & 0x3f];
+ output[op++] = encode[v & 0x3f];
+ if (do_padding) {
+ output[op++] = '=';
+ }
+ if (do_newline) {
+ if (do_cr) output[op++] = '\r';
+ output[op++] = '\n';
+ }
+ } else if (do_newline && op > 0 && count != LINE_GROUPS) {
+ if (do_cr) output[op++] = '\r';
+ output[op++] = '\n';
+ }
+
+ assert op == output.length;
+ return output;
+ }
+}
diff --git a/common/tests/src/com/android/common/Base64Test.java b/common/tests/src/com/android/common/Base64Test.java
new file mode 100644
index 0000000..39c4b20
--- /dev/null
+++ b/common/tests/src/com/android/common/Base64Test.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.common;
+
+import junit.framework.TestCase;
+
+public class Base64Test extends TestCase {
+ private static final String TAG = "B64Test";
+
+ /** Decodes a string, returning a string. */
+ private String decodeString(String in) throws Exception {
+ byte[] out = Base64.decode(in, 0);
+ return new String(out);
+ }
+
+ /**
+ * Encodes the string 'in' using 'flags'. Asserts that decoding
+ * gives the same string. Returns the encoded string.
+ */
+ private String encodeString(String in, int flags) throws Exception {
+ String b64 = Base64.encodeString(in.getBytes(), flags);
+ String dec = decodeString(b64);
+ assertEquals(in, dec);
+ return b64;
+ }
+
+ /** Assert that decoding 'in' throws IllegalArgumentException. */
+ private void assertBad(String in) throws Exception {
+ try {
+ byte[] out = Base64.decode(in, 0);
+ fail("should have failed to decode");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ /** Assert that actual equals the first len bytes of expected. */
+ private void assertEquals(byte[] expected, int len, byte[] actual) {
+ assertEquals(len, actual.length);
+ for (int i = 0; i < len; ++i) {
+ assertEquals(expected[i], actual[i]);
+ }
+ }
+
+ public void testDecodeExtraChars() throws Exception {
+ // padding 0
+ assertEquals("hello, world", decodeString("aGVsbG8sIHdvcmxk"));
+ assertBad("aGVsbG8sIHdvcmxk=");
+ assertBad("aGVsbG8sIHdvcmxk==");
+ assertBad("aGVsbG8sIHdvcmxk =");
+ assertBad("aGVsbG8sIHdvcmxk = = ");
+ assertEquals("hello, world", decodeString(" aGVs bG8s IHdv cmxk "));
+ assertEquals("hello, world", decodeString(" aGV sbG8 sIHd vcmx k "));
+ assertEquals("hello, world", decodeString(" aG VsbG 8sIH dvcm xk "));
+ assertEquals("hello, world", decodeString(" a GVsb G8sI Hdvc mxk "));
+ assertEquals("hello, world", decodeString(" a G V s b G 8 s I H d v c m x k "));
+ assertEquals("hello, world", decodeString("_a*G_V*s_b*G_8*s_I*H_d*v_c*m_x*k_"));
+ assertEquals("hello, world", decodeString("aGVsbG8sIHdvcmxk"));
+
+ // padding 1
+ assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPyE="));
+ assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPyE"));
+ assertBad("aGVsbG8sIHdvcmxkPyE==");
+ assertBad("aGVsbG8sIHdvcmxkPyE ==");
+ assertBad("aGVsbG8sIHdvcmxkPyE = = ");
+ assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E="));
+ assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E"));
+ assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E ="));
+ assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E "));
+ assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E = "));
+ assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E "));
+
+ // padding 2
+ assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkLg=="));
+ assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkLg"));
+ assertBad("aGVsbG8sIHdvcmxkLg=");
+ assertBad("aGVsbG8sIHdvcmxkLg =");
+ assertBad("aGVsbG8sIHdvcmxkLg = ");
+ assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g=="));
+ assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g"));
+ assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g =="));
+ assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g "));
+ assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g = = "));
+ assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g "));
+ }
+
+ private static final byte[] BYTES = { (byte) 0xff, (byte) 0xee, (byte) 0xdd,
+ (byte) 0xcc, (byte) 0xbb, (byte) 0xaa,
+ (byte) 0x99, (byte) 0x88, (byte) 0x77 };
+
+ public void testBinaryDecode() throws Exception {
+ assertEquals(BYTES, 0, Base64.decode("", 0));
+ assertEquals(BYTES, 1, Base64.decode("/w==", 0));
+ assertEquals(BYTES, 2, Base64.decode("/+4=", 0));
+ assertEquals(BYTES, 3, Base64.decode("/+7d", 0));
+ assertEquals(BYTES, 4, Base64.decode("/+7dzA==", 0));
+ assertEquals(BYTES, 5, Base64.decode("/+7dzLs=", 0));
+ assertEquals(BYTES, 6, Base64.decode("/+7dzLuq", 0));
+ assertEquals(BYTES, 7, Base64.decode("/+7dzLuqmQ==", 0));
+ assertEquals(BYTES, 8, Base64.decode("/+7dzLuqmYg=", 0));
+ }
+
+ public void testWebSafe() throws Exception {
+ assertEquals(BYTES, 0, Base64.decode("", Base64.WEB_SAFE));
+ assertEquals(BYTES, 1, Base64.decode("_w==", Base64.WEB_SAFE));
+ assertEquals(BYTES, 2, Base64.decode("_-4=", Base64.WEB_SAFE));
+ assertEquals(BYTES, 3, Base64.decode("_-7d", Base64.WEB_SAFE));
+ assertEquals(BYTES, 4, Base64.decode("_-7dzA==", Base64.WEB_SAFE));
+ assertEquals(BYTES, 5, Base64.decode("_-7dzLs=", Base64.WEB_SAFE));
+ assertEquals(BYTES, 6, Base64.decode("_-7dzLuq", Base64.WEB_SAFE));
+ assertEquals(BYTES, 7, Base64.decode("_-7dzLuqmQ==", Base64.WEB_SAFE));
+ assertEquals(BYTES, 8, Base64.decode("_-7dzLuqmYg=", Base64.WEB_SAFE));
+
+ assertEquals("", Base64.encodeString(BYTES, 0, 0, Base64.WEB_SAFE));
+ assertEquals("_w==\n", Base64.encodeString(BYTES, 0, 1, Base64.WEB_SAFE));
+ assertEquals("_-4=\n", Base64.encodeString(BYTES, 0, 2, Base64.WEB_SAFE));
+ assertEquals("_-7d\n", Base64.encodeString(BYTES, 0, 3, Base64.WEB_SAFE));
+ assertEquals("_-7dzA==\n", Base64.encodeString(BYTES, 0, 4, Base64.WEB_SAFE));
+ assertEquals("_-7dzLs=\n", Base64.encodeString(BYTES, 0, 5, Base64.WEB_SAFE));
+ assertEquals("_-7dzLuq\n", Base64.encodeString(BYTES, 0, 6, Base64.WEB_SAFE));
+ assertEquals("_-7dzLuqmQ==\n", Base64.encodeString(BYTES, 0, 7, Base64.WEB_SAFE));
+ assertEquals("_-7dzLuqmYg=\n", Base64.encodeString(BYTES, 0, 8, Base64.WEB_SAFE));
+ }
+
+ public void testFlags() throws Exception {
+ assertEquals("YQ==\n", encodeString("a", 0));
+ assertEquals("YQ==", encodeString("a", Base64.NO_WRAP));
+ assertEquals("YQ\n", encodeString("a", Base64.NO_PADDING));
+ assertEquals("YQ", encodeString("a", Base64.NO_PADDING | Base64.NO_WRAP));
+ assertEquals("YQ==\r\n", encodeString("a", Base64.CRLF));
+ assertEquals("YQ\r\n", encodeString("a", Base64.CRLF | Base64.NO_PADDING));
+
+ assertEquals("YWI=\n", encodeString("ab", 0));
+ assertEquals("YWI=", encodeString("ab", Base64.NO_WRAP));
+ assertEquals("YWI\n", encodeString("ab", Base64.NO_PADDING));
+ assertEquals("YWI", encodeString("ab", Base64.NO_PADDING | Base64.NO_WRAP));
+ assertEquals("YWI=\r\n", encodeString("ab", Base64.CRLF));
+ assertEquals("YWI\r\n", encodeString("ab", Base64.CRLF | Base64.NO_PADDING));
+
+ assertEquals("YWJj\n", encodeString("abc", 0));
+ assertEquals("YWJj", encodeString("abc", Base64.NO_WRAP));
+ assertEquals("YWJj\n", encodeString("abc", Base64.NO_PADDING));
+ assertEquals("YWJj", encodeString("abc", Base64.NO_PADDING | Base64.NO_WRAP));
+ assertEquals("YWJj\r\n", encodeString("abc", Base64.CRLF));
+ assertEquals("YWJj\r\n", encodeString("abc", Base64.CRLF | Base64.NO_PADDING));
+
+ assertEquals("YWJjZA==\n", encodeString("abcd", 0));
+ assertEquals("YWJjZA==", encodeString("abcd", Base64.NO_WRAP));
+ assertEquals("YWJjZA\n", encodeString("abcd", Base64.NO_PADDING));
+ assertEquals("YWJjZA", encodeString("abcd", Base64.NO_PADDING | Base64.NO_WRAP));
+ assertEquals("YWJjZA==\r\n", encodeString("abcd", Base64.CRLF));
+ assertEquals("YWJjZA\r\n", encodeString("abcd", Base64.CRLF | Base64.NO_PADDING));
+ }
+
+ public void testLineLength() throws Exception {
+ String in_56 = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd";
+ String in_57 = in_56 + "e";
+ String in_58 = in_56 + "ef";
+ String in_59 = in_56 + "efg";
+ String in_60 = in_56 + "efgh";
+ String in_61 = in_56 + "efghi";
+
+ String prefix = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFi";
+ String out_56 = prefix + "Y2Q=\n";
+ String out_57 = prefix + "Y2Rl\n";
+ String out_58 = prefix + "Y2Rl\nZg==\n";
+ String out_59 = prefix + "Y2Rl\nZmc=\n";
+ String out_60 = prefix + "Y2Rl\nZmdo\n";
+ String out_61 = prefix + "Y2Rl\nZmdoaQ==\n";
+
+ // no newline for an empty input array.
+ assertEquals("", encodeString("", 0));
+
+ assertEquals(out_56, encodeString(in_56, 0));
+ assertEquals(out_57, encodeString(in_57, 0));
+ assertEquals(out_58, encodeString(in_58, 0));
+ assertEquals(out_59, encodeString(in_59, 0));
+ assertEquals(out_60, encodeString(in_60, 0));
+ assertEquals(out_61, encodeString(in_61, 0));
+
+ assertEquals(out_56.replaceAll("=", ""), encodeString(in_56, Base64.NO_PADDING));
+ assertEquals(out_57.replaceAll("=", ""), encodeString(in_57, Base64.NO_PADDING));
+ assertEquals(out_58.replaceAll("=", ""), encodeString(in_58, Base64.NO_PADDING));
+ assertEquals(out_59.replaceAll("=", ""), encodeString(in_59, Base64.NO_PADDING));
+ assertEquals(out_60.replaceAll("=", ""), encodeString(in_60, Base64.NO_PADDING));
+ assertEquals(out_61.replaceAll("=", ""), encodeString(in_61, Base64.NO_PADDING));
+
+ assertEquals(out_56.replaceAll("\n", ""), encodeString(in_56, Base64.NO_WRAP));
+ assertEquals(out_57.replaceAll("\n", ""), encodeString(in_57, Base64.NO_WRAP));
+ assertEquals(out_58.replaceAll("\n", ""), encodeString(in_58, Base64.NO_WRAP));
+ assertEquals(out_59.replaceAll("\n", ""), encodeString(in_59, Base64.NO_WRAP));
+ assertEquals(out_60.replaceAll("\n", ""), encodeString(in_60, Base64.NO_WRAP));
+ assertEquals(out_61.replaceAll("\n", ""), encodeString(in_61, Base64.NO_WRAP));
+ }
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 10fef0d..56e44c8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -61,6 +61,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewManager;
+import android.view.ViewRoot;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -291,7 +292,7 @@
if (mAppDir == null) {
if (mSystemContext == null) {
mSystemContext =
- ApplicationContext.createSystemContext(mainThread);
+ ContextImpl.createSystemContext(mainThread);
mSystemContext.getResources().updateConfiguration(
mainThread.getConfiguration(),
mainThread.getDisplayMetricsLocked(false));
@@ -512,7 +513,7 @@
try {
java.lang.ClassLoader cl = getClassLoader();
- ApplicationContext appContext = new ApplicationContext();
+ ContextImpl appContext = new ContextImpl();
appContext.init(this, null, mActivityThread);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
@@ -1144,7 +1145,7 @@
}
}
- private static ApplicationContext mSystemContext = null;
+ private static ContextImpl mSystemContext = null;
private static final class ActivityRecord {
IBinder token;
@@ -1307,7 +1308,7 @@
}
private static final class ContextCleanupInfo {
- ApplicationContext context;
+ ContextImpl context;
String what;
String who;
}
@@ -1628,7 +1629,7 @@
long dalvikAllocated = dalvikMax - dalvikFree;
long viewInstanceCount = ViewDebug.getViewInstanceCount();
long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount();
- long appContextInstanceCount = ApplicationContext.getInstanceCount();
+ long appContextInstanceCount = ContextImpl.getInstanceCount();
long activityInstanceCount = Activity.getInstanceCount();
int globalAssetCount = AssetManager.getGlobalAssetCount();
int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
@@ -1813,6 +1814,7 @@
public static final int DESTROY_BACKUP_AGENT = 129;
public static final int SUICIDE = 130;
public static final int REMOVE_PROVIDER = 131;
+ public static final int ENABLE_JIT = 132;
String codeToString(int code) {
if (localLOGV) {
switch (code) {
@@ -1848,6 +1850,7 @@
case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
case SUICIDE: return "SUICIDE";
case REMOVE_PROVIDER: return "REMOVE_PROVIDER";
+ case ENABLE_JIT: return "ENABLE_JIT";
}
}
return "(unknown)";
@@ -1965,6 +1968,9 @@
case REMOVE_PROVIDER:
completeRemoveProvider((IContentProvider)msg.obj);
break;
+ case ENABLE_JIT:
+ ensureJitEnabled();
+ break;
}
}
@@ -2000,6 +2006,7 @@
prev.nextIdle = null;
} while (a != null);
}
+ ensureJitEnabled();
return false;
}
}
@@ -2064,6 +2071,7 @@
String mInstrumentationAppPackage = null;
String mInstrumentedAppDir = null;
boolean mSystemThread = false;
+ boolean mJitEnabled = false;
/**
* Activities that are enqueued to be relaunched. This list is accessed
@@ -2245,11 +2253,11 @@
return mBoundApplication.processName;
}
- public ApplicationContext getSystemContext() {
+ public ContextImpl getSystemContext() {
synchronized (this) {
if (mSystemContext == null) {
- ApplicationContext context =
- ApplicationContext.createSystemContext(this);
+ ContextImpl context =
+ ContextImpl.createSystemContext(this);
PackageInfo info = new PackageInfo(this, "android", context, null);
context.init(info, null, this);
context.getResources().updateConfiguration(
@@ -2264,11 +2272,18 @@
public void installSystemApplicationInfo(ApplicationInfo info) {
synchronized (this) {
- ApplicationContext context = getSystemContext();
+ ContextImpl context = getSystemContext();
context.init(new PackageInfo(this, "android", context, info), null, this);
}
}
+ void ensureJitEnabled() {
+ if (!mJitEnabled) {
+ mJitEnabled = true;
+ dalvik.system.VMRuntime.getRuntime().startJitCompilation();
+ }
+ }
+
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
@@ -2372,7 +2387,7 @@
}
}
- final void scheduleContextCleanup(ApplicationContext context, String who,
+ final void scheduleContextCleanup(ContextImpl context, String who,
String what) {
ContextCleanupInfo cci = new ContextCleanupInfo();
cci.context = context;
@@ -2431,7 +2446,7 @@
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
- ApplicationContext appContext = new ApplicationContext();
+ ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);
appContext.setOuterContext(activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
@@ -2628,7 +2643,7 @@
+ ", comp=" + data.intent.getComponent().toShortString()
+ ", dir=" + packageInfo.getAppDir());
- ApplicationContext context = (ApplicationContext)app.getBaseContext();
+ ContextImpl context = (ContextImpl)app.getBaseContext();
receiver.setOrderedHint(true);
receiver.setResult(data.resultCode, data.resultData,
data.resultExtras);
@@ -2697,7 +2712,7 @@
if (DEBUG_BACKUP) Log.v(TAG, "Initializing BackupAgent "
+ data.appInfo.backupAgentName);
- ApplicationContext context = new ApplicationContext();
+ ContextImpl context = new ContextImpl();
context.init(packageInfo, null, this);
context.setOuterContext(agent);
agent.attach(context);
@@ -2769,7 +2784,7 @@
try {
if (localLOGV) Log.v(TAG, "Creating service " + data.info.name);
- ApplicationContext context = new ApplicationContext();
+ ContextImpl context = new ContextImpl();
context.init(packageInfo, null, this);
Application app = packageInfo.makeApplication(false, mInstrumentation);
@@ -2808,6 +2823,7 @@
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
}
+ ensureJitEnabled();
} catch (RemoteException ex) {
}
} catch (Exception e) {
@@ -2876,6 +2892,7 @@
} catch (RemoteException e) {
// nothing to do.
}
+ ensureJitEnabled();
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
@@ -2893,9 +2910,9 @@
if (localLOGV) Log.v(TAG, "Destroying service " + s);
s.onDestroy();
Context context = s.getBaseContext();
- if (context instanceof ApplicationContext) {
+ if (context instanceof ContextImpl) {
final String who = s.getClassName();
- ((ApplicationContext) context).scheduleFinalCleanup(who, "Service");
+ ((ContextImpl) context).scheduleFinalCleanup(who, "Service");
}
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
@@ -3510,8 +3527,8 @@
// ApplicationContext we need to have it tear down things
// cleanly.
Context c = r.activity.getBaseContext();
- if (c instanceof ApplicationContext) {
- ((ApplicationContext) c).scheduleFinalCleanup(
+ if (c instanceof ContextImpl) {
+ ((ContextImpl) c).scheduleFinalCleanup(
r.activity.getClass().getName(), "Activity");
}
}
@@ -3773,7 +3790,7 @@
Resources.updateSystemConfiguration(config, dm);
- ApplicationContext.ApplicationPackageManager.configurationChanged();
+ ContextImpl.ApplicationPackageManager.configurationChanged();
//Log.i(TAG, "Configuration changed in " + currentPackageName());
{
Iterator<WeakReference<Resources>> it =
@@ -3864,10 +3881,6 @@
mBoundApplication = data;
mConfiguration = new Configuration(data.config);
- // We now rely on this being set by zygote.
- //Process.setGid(data.appInfo.gid);
- //Process.setUid(data.appInfo.uid);
-
// send up app name; do this *before* waiting for debugger
Process.setArgV0(data.processName);
android.ddm.DdmHandleAppName.setAppName(data.processName);
@@ -3929,7 +3942,7 @@
}
if (data.instrumentationName != null) {
- ApplicationContext appContext = new ApplicationContext();
+ ContextImpl appContext = new ContextImpl();
appContext.init(data.info, null, this);
InstrumentationInfo ii = null;
try {
@@ -3954,7 +3967,7 @@
instrApp.dataDir = ii.dataDir;
PackageInfo pi = getPackageInfo(instrApp,
appContext.getClassLoader(), false, true);
- ApplicationContext instrContext = new ApplicationContext();
+ ContextImpl instrContext = new ContextImpl();
instrContext.init(pi, null, this);
try {
@@ -3998,6 +4011,9 @@
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
+ // For process that contain content providers, we want to
+ // ensure that the JIT is enabled "at some point".
+ mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
try {
@@ -4303,6 +4319,11 @@
sThreadLocal.set(this);
mSystemThread = system;
if (!system) {
+ ViewRoot.addFirstDrawHandler(new Runnable() {
+ public void run() {
+ ensureJitEnabled();
+ }
+ });
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");
RuntimeInit.setApplicationObject(mAppThread.asBinder());
IActivityManager mgr = ActivityManagerNative.getDefault();
@@ -4316,7 +4337,7 @@
android.ddm.DdmHandleAppName.setAppName("system_process");
try {
mInstrumentation = new Instrumentation();
- ApplicationContext context = new ApplicationContext();
+ ContextImpl context = new ContextImpl();
context.init(getSystemContext().mPackageInfo, null, this);
Application app = Instrumentation.newApplication(Application.class, context);
mAllApplications.add(app);
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ContextImpl.java
similarity index 99%
rename from core/java/android/app/ApplicationContext.java
rename to core/java/android/app/ContextImpl.java
index cf6e0e7..5f89496 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ContextImpl.java
@@ -148,10 +148,10 @@
}
/**
- * Common implementation of Context API, which Activity and other application
- * classes inherit.
+ * Common implementation of Context API, which provides the base
+ * context object for Activity and other application components.
*/
-class ApplicationContext extends Context {
+class ContextImpl extends Context {
private final static String TAG = "ApplicationContext";
private final static boolean DEBUG = false;
private final static boolean DEBUG_ICONS = false;
@@ -1328,13 +1328,13 @@
public Context createPackageContext(String packageName, int flags)
throws PackageManager.NameNotFoundException {
if (packageName.equals("system") || packageName.equals("android")) {
- return new ApplicationContext(mMainThread.getSystemContext());
+ return new ContextImpl(mMainThread.getSystemContext());
}
ActivityThread.PackageInfo pi =
mMainThread.getPackageInfo(packageName, flags);
if (pi != null) {
- ApplicationContext c = new ApplicationContext();
+ ContextImpl c = new ContextImpl();
c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
c.init(pi, null, mMainThread, mResources);
if (c.mResources != null) {
@@ -1371,13 +1371,13 @@
return file;
}
- static ApplicationContext createSystemContext(ActivityThread mainThread) {
- ApplicationContext context = new ApplicationContext();
+ static ContextImpl createSystemContext(ActivityThread mainThread) {
+ ContextImpl context = new ContextImpl();
context.init(Resources.getSystem(), mainThread);
return context;
}
- ApplicationContext() {
+ ContextImpl() {
++sInstanceCount;
mOuterContext = this;
}
@@ -1388,7 +1388,7 @@
*
* @param context Existing application context.
*/
- public ApplicationContext(ApplicationContext context) {
+ public ContextImpl(ContextImpl context) {
++sInstanceCount;
mPackageInfo = context.mPackageInfo;
mResources = context.mResources;
@@ -2124,7 +2124,7 @@
}
}
- ApplicationPackageManager(ApplicationContext context,
+ ApplicationPackageManager(ContextImpl context,
IPackageManager pm) {
mContext = context;
mPM = pm;
@@ -2656,7 +2656,7 @@
}
}
- private final ApplicationContext mContext;
+ private final ContextImpl mContext;
private final IPackageManager mPM;
private static final Object sSync = new Object();
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index ec9f3b4..7fa5b08 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -60,6 +60,7 @@
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
@@ -105,7 +106,7 @@
// views & widgets
private TextView mBadgeLabel;
- private SearchSourceSelector mSourceSelector;
+ private ImageView mAppIcon;
private SearchAutoComplete mSearchAutoComplete;
private Button mGoButton;
private ImageButton mVoiceButton;
@@ -209,8 +210,7 @@
mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge);
mSearchAutoComplete = (SearchAutoComplete)
findViewById(com.android.internal.R.id.search_src_text);
- mSourceSelector = new SearchSourceSelector(
- findViewById(com.android.internal.R.id.search_source_selector));
+ mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon);
mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn);
mSearchPlate = findViewById(com.android.internal.R.id.search_plate);
@@ -609,16 +609,13 @@
}
private void updateSearchAppIcon() {
- mSourceSelector.setSource(mSearchable.getSearchActivity());
- mSourceSelector.setAppSearchData(mAppSearchData);
-
// In Donut, we special-case the case of the browser to hide the app icon as if it were
// global search, for extra space for url entry.
//
// TODO: Remove this special case once the issue has been reconciled in Eclair.
if (mGlobalSearchMode || isBrowserSearch()) {
- mSourceSelector.setSourceIcon(null);
- mSourceSelector.setVisibility(View.GONE);
+ mAppIcon.setImageResource(0);
+ mAppIcon.setVisibility(View.GONE);
mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL,
mSearchPlate.getPaddingTop(),
mSearchPlate.getPaddingRight(),
@@ -634,8 +631,8 @@
icon = pm.getDefaultActivityIcon();
Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon");
}
- mSourceSelector.setSourceIcon(icon);
- mSourceSelector.setVisibility(View.VISIBLE);
+ mAppIcon.setImageDrawable(icon);
+ mAppIcon.setVisibility(View.VISIBLE);
mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL,
mSearchPlate.getPaddingTop(),
mSearchPlate.getPaddingRight(),
@@ -818,7 +815,6 @@
if (!mSearchAutoComplete.isPerformingCompletion()) {
// The user changed the query, remember it.
mUserQuery = s == null ? "" : s.toString();
- mSourceSelector.setQuery(mUserQuery);
}
}
@@ -1932,7 +1928,6 @@
query = "";
}
mUserQuery = query;
- mSourceSelector.setQuery(query);
mSearchAutoComplete.setText(query);
mSearchAutoComplete.setSelection(query.length());
}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 12a4347..3046a2c 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1604,15 +1604,6 @@
public final static String SUGGEST_PARAMETER_LIMIT = "limit";
/**
- * Intent action for opening the search source selection activity.
- * The intent may include these extra values:
- * {@link #QUERY},
- * {@link #APP_DATA}.
- */
- public static final String INTENT_ACTION_SELECT_SEARCH_SOURCE
- = "android.intent.action.SELECT_SEARCH_SOURCE";
-
- /**
* If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
* the search dialog will switch to a different suggestion source when the
* suggestion is clicked.
diff --git a/core/java/android/app/SearchSourceSelector.java b/core/java/android/app/SearchSourceSelector.java
deleted file mode 100644
index fabf858..0000000
--- a/core/java/android/app/SearchSourceSelector.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * 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.app;
-
-import com.android.internal.R;
-
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageButton;
-
-import java.util.List;
-
-/**
- * Utilities for setting up the search source selector.
- *
- * This class has two copies:
- * android.app.SearchSourceSelector
- * com.android.quicksearchbox.ui.SearchSourceSelector
- *
- * They should keep the same look and feel as much as possible,
- * but only the intent details must absolutely stay in sync.
- *
- * @hide
- */
-public class SearchSourceSelector implements View.OnClickListener {
-
- private static final String TAG = "SearchSourceSelector";
-
- // TODO: This should be defined in android.provider.Applications,
- // and have a less made-up value.
- private static final String APPLICATION_TYPE = "application/vnd.android.application";
-
- public static final int ICON_VIEW_ID = R.id.search_source_selector_icon;
-
- private final View mView;
-
- private final ImageButton mIconView;
-
- private ComponentName mSource;
-
- private Bundle mAppSearchData;
-
- private String mQuery;
-
- public SearchSourceSelector(View view) {
- mView = view;
- mIconView = (ImageButton) view.findViewById(ICON_VIEW_ID);
- mIconView.setOnClickListener(this);
- }
-
- /**
- * Sets the icon displayed in the search source selector.
- */
- public void setSourceIcon(Drawable icon) {
- mIconView.setImageDrawable(icon);
- }
-
- /**
- * Sets the current search source.
- */
- public void setSource(ComponentName source) {
- mSource = source;
- }
-
- /**
- * Sets the app-specific data that will be passed to the search activity if
- * the user opens the source selector and chooses a source.
- */
- public void setAppSearchData(Bundle appSearchData) {
- mAppSearchData = appSearchData;
- }
-
- /**
- * Sets the initial query that will be passed to the search activity if
- * the user opens the source selector and chooses a source.
- */
- public void setQuery(String query) {
- mQuery = query;
- }
-
- public void setVisibility(int visibility) {
- mView.setVisibility(visibility);
- }
-
- /**
- * Creates an intent for opening the search source selector activity.
- *
- * @param source The current search source.
- * @param query The initial query that will be passed to the search activity if
- * the user opens the source selector and chooses a source.
- * @param appSearchData The app-specific data that will be passed to the search
- * activity if the user opens the source selector and chooses a source.
- */
- public static Intent createIntent(ComponentName source, String query, Bundle appSearchData) {
- Intent intent = new Intent(SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- Uri sourceUri = componentNameToUri(source);
- if (sourceUri != null) {
- intent.setDataAndType(sourceUri, APPLICATION_TYPE);
- }
- if (query != null) {
- intent.putExtra(SearchManager.QUERY, query);
- }
- if (query != null) {
- intent.putExtra(SearchManager.APP_DATA, appSearchData);
- }
- return intent;
- }
-
- /**
- * Gets the search source from which the given
- * {@link SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE} intent was sent.
- */
- public static ComponentName getSource(Intent intent) {
- return uriToComponentName(intent.getData());
- }
-
- private static Uri componentNameToUri(ComponentName name) {
- if (name == null) return null;
- // TODO: This URI format is specificed in android.provider.Applications which is @hidden
- return new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority("applications")
- .appendEncodedPath("applications")
- .appendPath(name.getPackageName())
- .appendPath(name.getClassName())
- .build();
- }
-
- private static ComponentName uriToComponentName(Uri uri) {
- if (uri == null) return null;
- List<String> path = uri.getPathSegments();
- if (path == null || path.size() != 3) return null;
- String pkg = path.get(1);
- String cls = path.get(2);
- if (TextUtils.isEmpty(pkg) || TextUtils.isEmpty(cls)) return null;
- return new ComponentName(pkg, cls);
- }
-
- public void onClick(View v) {
- trigger();
- }
-
- private void trigger() {
- try {
- Intent intent = createIntent(mSource, mQuery, mAppSearchData);
- intent.setSourceBounds(getOnScreenRect(mIconView));
- mIconView.getContext().startActivity(intent);
- } catch (ActivityNotFoundException ex) {
- Log.e(TAG, "No source selector activity found", ex);
- }
- }
-
- // TODO: This code is replicated in lots of places:
- // - android.provider.ContactsContract.QuickContact.showQuickContact()
- // - android.widget.RemoteViews.setOnClickPendingIntent()
- // - com.android.launcher2.Launcher.onClick()
- // - com.android.launcher.Launcher.onClick()
- // - com.android.server.status.StatusBarService.Launcher.onClick()
- private static Rect getOnScreenRect(View v) {
- final float appScale = v.getResources().getCompatibilityInfo().applicationScale;
- final int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- final Rect rect = new Rect();
- rect.left = (int) (pos[0] * appScale + 0.5f);
- rect.top = (int) (pos[1] * appScale + 0.5f);
- rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
- rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
- return rect;
- }
-
-}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 51d7393..72ec616 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -31,12 +31,12 @@
public class StatusBarManager {
/**
* Flag for {@link #disable} to make the status bar not expandable. Unless you also
- * set {@link #DISABLE_NOTIFICATIONS}, new notifications will continue to show.
+ * set {@link #DISABLE_NOTIFICATION_ICONS}, new notifications will continue to show.
*/
public static final int DISABLE_EXPAND = 0x00000001;
/**
- * Flag for {@link #disable} to hide notification icons and ticker text.
+ * Flag for {@link #disable} to hide notification icons and scrolling ticker text.
*/
public static final int DISABLE_NOTIFICATION_ICONS = 0x00000002;
@@ -47,6 +47,12 @@
public static final int DISABLE_NOTIFICATION_ALERTS = 0x00000004;
/**
+ * Flag for {@link #disable} to hide only the scrolling ticker. Note that
+ * {@link #DISABLE_NOTIFICATION_ICONS} implies {@link #DISABLE_NOTIFICATION_TICKER}.
+ */
+ public static final int DISABLE_NOTIFICATION_TICKER = 0x00000008;
+
+ /**
* Re-enable all of the status bar features that you've disabled.
*/
public static final int DISABLE_NONE = 0x00000000;
diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl
index fd40d98..bead395 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/backup/IRestoreSession.aidl
@@ -40,6 +40,8 @@
* Restore the given set onto the device, replacing the current data of any app
* contained in the restore set with the data previously backed up.
*
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ *
* @return Zero on success; nonzero on error. The observer will only receive
* progress callbacks if this method returned zero.
* @param token The token from {@link getAvailableRestoreSets()} corresponding to
@@ -47,7 +49,24 @@
* @param observer If non-null, this binder points to an object that will receive
* progress callbacks during the restore operation.
*/
- int performRestore(long token, IRestoreObserver observer);
+ int restoreAll(long token, IRestoreObserver observer);
+
+ /**
+ * Restore a single application from backup. The data will be restored from the
+ * current backup dataset if the given package has stored data there, or from
+ * the dataset used during the last full device setup operation if the current
+ * backup dataset has no matching data. If no backup data exists for this package
+ * in either source, a nonzero value will be returned.
+ *
+ * @return Zero on success; nonzero on error. The observer will only receive
+ * progress callbacks if this method returned zero.
+ * @param packageName The name of the package whose data to restore. If this is
+ * not the name of the caller's own package, then the android.permission.BACKUP
+ * permission must be held.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
+ */
+ int restorePackage(in String packageName, IRestoreObserver observer);
/**
* End this restore session. After this method is called, the IRestoreSession binder
diff --git a/core/java/android/backup/RestoreSession.java b/core/java/android/backup/RestoreSession.java
index 6b35fe8..d10831e 100644
--- a/core/java/android/backup/RestoreSession.java
+++ b/core/java/android/backup/RestoreSession.java
@@ -58,25 +58,56 @@
* Restore the given set onto the device, replacing the current data of any app
* contained in the restore set with the data previously backed up.
*
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ *
* @return Zero on success; nonzero on error. The observer will only receive
* progress callbacks if this method returned zero.
- * @param token The token from {@link #getAvailableRestoreSets()} corresponding to
+ * @param token The token from {@link getAvailableRestoreSets()} corresponding to
* the restore set that should be used.
- * @param observer If non-null, this argument points to an object that will receive
- * progress callbacks during the restore operation. These callbacks will occur
- * on the main thread of the application.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
*/
- public int performRestore(long token, RestoreObserver observer) {
+ public int restoreAll(long token, RestoreObserver observer) {
int err = -1;
if (mObserver != null) {
- Log.d(TAG, "performRestore() called during active restore");
+ Log.d(TAG, "restoreAll() called during active restore");
return -1;
}
mObserver = new RestoreObserverWrapper(mContext, observer);
try {
- err = mBinder.performRestore(token, mObserver);
+ err = mBinder.restoreAll(token, mObserver);
} catch (RemoteException e) {
- Log.d(TAG, "Can't contact server to perform restore");
+ Log.d(TAG, "Can't contact server to restore");
+ }
+ return err;
+ }
+
+ /**
+ * Restore a single application from backup. The data will be restored from the
+ * current backup dataset if the given package has stored data there, or from
+ * the dataset used during the last full device setup operation if the current
+ * backup dataset has no matching data. If no backup data exists for this package
+ * in either source, a nonzero value will be returned.
+ *
+ * @return Zero on success; nonzero on error. The observer will only receive
+ * progress callbacks if this method returned zero.
+ * @param packageName The name of the package whose data to restore. If this is
+ * not the name of the caller's own package, then the android.permission.BACKUP
+ * permission must be held.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
+ */
+ public int restorePackage(String packageName, RestoreObserver observer) {
+ int err = -1;
+ if (mObserver != null) {
+ Log.d(TAG, "restorePackage() called during active restore");
+ return -1;
+ }
+ mObserver = new RestoreObserverWrapper(mContext, observer);
+ try {
+ err = mBinder.restorePackage(packageName, mObserver);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Can't contact server to restore package");
}
return err;
}
@@ -87,7 +118,7 @@
*
* <p><b>Note:</b> The caller <i>must</i> invoke this method to end the restore session,
* even if {@link #getAvailableRestoreSets()} or
- * {@link #performRestore(long, RestoreObserver)} failed.
+ * {@link #restorePackage(long, String, RestoreObserver)} failed.
*/
public void endRestoreSession() {
try {
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
index d71344c..300cd28 100755
--- a/core/java/android/gesture/Gesture.java
+++ b/core/java/android/gesture/Gesture.java
@@ -293,7 +293,7 @@
} catch (IOException e) {
Log.e(GestureConstants.LOG_TAG, "Error reading Gesture from parcel:", e);
} finally {
- GestureUtilities.closeStream(inStream);
+ GestureUtils.closeStream(inStream);
}
if (gesture != null) {
@@ -322,8 +322,8 @@
} catch (IOException e) {
Log.e(GestureConstants.LOG_TAG, "Error writing Gesture to parcel:", e);
} finally {
- GestureUtilities.closeStream(outStream);
- GestureUtilities.closeStream(byteStream);
+ GestureUtils.closeStream(outStream);
+ GestureUtils.closeStream(byteStream);
}
if (result) {
diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java
index 30ecf5a..b6c260f 100755
--- a/core/java/android/gesture/GestureOverlayView.java
+++ b/core/java/android/gesture/GestureOverlayView.java
@@ -638,7 +638,7 @@
if (mTotalLength > mGestureStrokeLengthThreshold) {
final OrientedBoundingBox box =
- GestureUtilities.computeOrientedBoundingBox(mStrokeBuffer);
+ GestureUtils.computeOrientedBoundingBox(mStrokeBuffer);
float angle = Math.abs(box.orientation);
if (angle > 90) {
diff --git a/core/java/android/gesture/GestureStore.java b/core/java/android/gesture/GestureStore.java
index 11a94d1..11b5044 100644
--- a/core/java/android/gesture/GestureStore.java
+++ b/core/java/android/gesture/GestureStore.java
@@ -264,7 +264,7 @@
mChanged = false;
} finally {
- if (closeStream) GestureUtilities.closeStream(out);
+ if (closeStream) GestureUtils.closeStream(out);
}
}
@@ -299,7 +299,7 @@
Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms");
}
} finally {
- if (closeStream) GestureUtilities.closeStream(in);
+ if (closeStream) GestureUtils.closeStream(in);
}
}
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index c3ddb28..1d0f0fe 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -159,15 +159,15 @@
* @return the path
*/
public Path toPath(float width, float height, int numSample) {
- final float[] pts = GestureUtilities.temporalSampling(this, numSample);
+ final float[] pts = GestureUtils.temporalSampling(this, numSample);
final RectF rect = boundingBox;
- GestureUtilities.translate(pts, -rect.left, -rect.top);
+ GestureUtils.translate(pts, -rect.left, -rect.top);
float sx = width / rect.width();
float sy = height / rect.height();
float scale = sx > sy ? sy : sx;
- GestureUtilities.scale(pts, scale, scale);
+ GestureUtils.scale(pts, scale, scale);
float mX = 0;
float mY = 0;
@@ -241,6 +241,6 @@
* @return OrientedBoundingBox
*/
public OrientedBoundingBox computeOrientedBoundingBox() {
- return GestureUtilities.computeOrientedBoundingBox(points);
+ return GestureUtils.computeOrientedBoundingBox(points);
}
}
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtils.java
similarity index 99%
rename from core/java/android/gesture/GestureUtilities.java
rename to core/java/android/gesture/GestureUtils.java
index 9d95ce4..dd221fc 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtils.java
@@ -36,12 +36,12 @@
* distances between two gestures).
* </ul>
*/
-public final class GestureUtilities {
+public final class GestureUtils {
private static final float SCALING_THRESHOLD = 0.26f;
private static final float NONUNIFORM_SCALE = (float) Math.sqrt(2);
- private GestureUtilities() {
+ private GestureUtils() {
}
/**
diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java
index bb0b340..02a6519 100755
--- a/core/java/android/gesture/Instance.java
+++ b/core/java/android/gesture/Instance.java
@@ -84,13 +84,13 @@
}
private static float[] spatialSampler(Gesture gesture) {
- return GestureUtilities.spatialSampling(gesture, PATCH_SAMPLE_SIZE, false);
+ return GestureUtils.spatialSampling(gesture, PATCH_SAMPLE_SIZE, false);
}
private static float[] temporalSampler(int orientationType, Gesture gesture) {
- float[] pts = GestureUtilities.temporalSampling(gesture.getStrokes().get(0),
+ float[] pts = GestureUtils.temporalSampling(gesture.getStrokes().get(0),
SEQUENCE_SAMPLE_SIZE);
- float[] center = GestureUtilities.computeCentroid(pts);
+ float[] center = GestureUtils.computeCentroid(pts);
float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]);
float adjustment = -orientation;
@@ -104,8 +104,8 @@
}
}
- GestureUtilities.translate(pts, -center[0], -center[1]);
- GestureUtilities.rotate(pts, adjustment);
+ GestureUtils.translate(pts, -center[0], -center[1]);
+ GestureUtils.rotate(pts, adjustment);
return pts;
}
diff --git a/core/java/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java
index 9987e69..7224ded 100644
--- a/core/java/android/gesture/InstanceLearner.java
+++ b/core/java/android/gesture/InstanceLearner.java
@@ -53,9 +53,9 @@
}
double distance;
if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) {
- distance = GestureUtilities.minimumCosineDistance(sample.vector, vector, orientationType);
+ distance = GestureUtils.minimumCosineDistance(sample.vector, vector, orientationType);
} else {
- distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector);
+ distance = GestureUtils.squaredEuclideanDistance(sample.vector, vector);
}
double weight;
if (distance == 0) {
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 0c6bb1a..8eed9f7 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -287,6 +287,7 @@
static private class SensorThread {
Thread mThread;
+ boolean mSensorsReady;
SensorThread() {
// this gets to the sensor module. We can have only one per process.
@@ -299,17 +300,28 @@
}
// must be called with sListeners lock
- void startLocked(ISensorService service) {
+ boolean startLocked(ISensorService service) {
try {
if (mThread == null) {
Bundle dataChannel = service.getDataChannel();
- mThread = new Thread(new SensorThreadRunnable(dataChannel),
- SensorThread.class.getName());
- mThread.start();
+ if (dataChannel != null) {
+ mSensorsReady = false;
+ SensorThreadRunnable runnable = new SensorThreadRunnable(dataChannel);
+ Thread thread = new Thread(runnable, SensorThread.class.getName());
+ thread.start();
+ synchronized (runnable) {
+ while (mSensorsReady == false) {
+ runnable.wait();
+ }
+ }
+ mThread = thread;
+ }
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in startLocked: ", e);
+ } catch (InterruptedException e) {
}
+ return mThread == null ? false : true;
}
private class SensorThreadRunnable implements Runnable {
@@ -319,13 +331,9 @@
}
private boolean open() {
- if (mDataChannel == null) {
- Log.e(TAG, "mDataChannel == NULL, exiting");
- synchronized (sListeners) {
- mThread = null;
- }
- return false;
- }
+ // NOTE: this cannot synchronize on sListeners, since
+ // it's held in the main thread at least until we
+ // return from here.
// this thread is guaranteed to be unique
Parcelable[] pfds = mDataChannel.getParcelableArray("fds");
@@ -370,6 +378,12 @@
return;
}
+ synchronized (this) {
+ // we've open the driver, we're ready to open the sensors
+ mSensorsReady = true;
+ this.notify();
+ }
+
while (true) {
// wait for an event
final int sensor = sensors_data_poll(values, status, timestamp);
@@ -907,14 +921,18 @@
String name = sensor.getName();
int handle = sensor.getHandle();
if (l == null) {
+ result = false;
l = new ListenerDelegate(listener, sensor, handler);
- result = mSensorService.enableSensor(l, name, handle, delay);
- if (result) {
- sListeners.add(l);
- sListeners.notify();
- }
+ sListeners.add(l);
if (!sListeners.isEmpty()) {
- sSensorThread.startLocked(mSensorService);
+ result = sSensorThread.startLocked(mSensorService);
+ if (result) {
+ result = mSensorService.enableSensor(l, name, handle, delay);
+ if (!result) {
+ // there was an error, remove the listeners
+ sListeners.remove(l);
+ }
+ }
}
} else {
result = mSensorService.enableSensor(l, name, handle, delay);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 30799ec..d435df5 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -116,6 +116,24 @@
"android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
/**
+ * Broadcast Action: A tetherable connection has come or gone
+ * TODO - finish the doc
+ * @hide
+ */
+ public static final String ACTION_TETHER_STATE_CHANGED =
+ "android.net.conn.TETHER_STATE_CHANGED";
+
+ /**
+ * @hide
+ */
+ public static final String EXTRA_AVAILABLE_TETHER_COUNT = "availableCount";
+
+ /**
+ * @hide
+ */
+ public static final String EXTRA_ACTIVE_TETHER_COUNT = "activeCount";
+
+ /**
* The Default Mobile data connection. When active, all data traffic
* will use this connection by default. Should not coexist with other
* default connections.
@@ -338,4 +356,48 @@
}
mService = service;
}
+
+ /**
+ * {@hide}
+ */
+ public String[] getTetherableIfaces() {
+ try {
+ return mService.getTetherableIfaces();
+ } catch (RemoteException e) {
+ return new String[0];
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public String[] getTetheredIfaces() {
+ try {
+ return mService.getTetheredIfaces();
+ } catch (RemoteException e) {
+ return new String[0];
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public boolean tether(String iface) {
+ try {
+ return mService.tether(iface);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public boolean untether(String iface) {
+ try {
+ return mService.untether(iface);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 9f59cce..caa3f2b 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -50,4 +50,12 @@
boolean getBackgroundDataSetting();
void setBackgroundDataSetting(boolean allowBackgroundData);
+
+ boolean tether(String iface);
+
+ boolean untether(String iface);
+
+ String[] getTetherableIfaces();
+
+ String[] getTetheredIfaces();
}
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 62e9f1f..ad8e2bf 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -54,7 +54,7 @@
* @return number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static long getMobileTxPkts() {
+ public static long getMobileTxPackets() {
return getMobileStat(MOBILE_TX_PACKETS);
}
@@ -64,7 +64,7 @@
* @return number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static long getMobileRxPkts() {
+ public static long getMobileRxPackets() {
return getMobileStat(MOBILE_RX_PACKETS);
}
@@ -94,7 +94,7 @@
* @return the number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static long getTotalTxPkts() {
+ public static long getTotalTxPackets() {
return getTotalStat("tx_packets");
}
@@ -104,7 +104,7 @@
* @return number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static long getTotalRxPkts() {
+ public static long getTotalRxPackets() {
return getTotalStat("rx_packets");
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index e4ec098..f48f45f 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -140,7 +140,8 @@
* Attaches a PPP server daemon to the specified TTY with the specified
* local/remote addresses.
*/
- void attachPppd(String tty, String localAddr, String remoteAddr);
+ void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
+ String dns2Addr);
/**
* Detaches a PPP server daemon from the specified TTY.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7128005..bacaf43 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2944,6 +2944,13 @@
public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
/**
+ * Whether or not a notification is displayed when a Tetherable interface is detected.
+ * (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String TETHER_NOTIFY = "tether_notify";
+
+ /**
* If nonzero, ANRs in invisible background processes bring up a dialog.
* Otherwise, the process will be silently killed.
* @hide
diff --git a/core/java/android/text/util/Rfc822Tokenizer.java b/core/java/android/text/util/Rfc822Tokenizer.java
index cb39f7d..9d8bfd9 100644
--- a/core/java/android/text/util/Rfc822Tokenizer.java
+++ b/core/java/android/text/util/Rfc822Tokenizer.java
@@ -41,7 +41,6 @@
* It will try to be tolerant of broken syntax instead of
* returning an error.
*
- * @hide
*/
public static void tokenize(CharSequence text, Collection<Rfc822Token> out) {
StringBuilder name = new StringBuilder();
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 094b7dd..07b2d1c 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -98,6 +98,9 @@
static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
+ static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
+ static boolean sFirstDrawComplete = false;
+
private static int sDrawTime;
long mLastTrackballTime = 0;
@@ -254,6 +257,14 @@
return sInstanceCount;
}
+ public static void addFirstDrawHandler(Runnable callback) {
+ synchronized (sFirstDrawHandlers) {
+ if (!sFirstDrawComplete) {
+ sFirstDrawHandlers.add(callback);
+ }
+ }
+ }
+
// FIXME for perf testing only
private boolean mProfile = false;
@@ -1189,6 +1200,15 @@
return;
}
+ if (!sFirstDrawComplete) {
+ synchronized (sFirstDrawHandlers) {
+ sFirstDrawComplete = true;
+ for (int i=0; i<sFirstDrawHandlers.size(); i++) {
+ post(sFirstDrawHandlers.get(i));
+ }
+ }
+ }
+
scrollToRectOrFocus(null, false);
if (mAttachInfo.mViewScrollChanged) {
diff --git a/core/java/android/webkit/CacheLoader.java b/core/java/android/webkit/CacheLoader.java
index de8f888..aeb537c 100644
--- a/core/java/android/webkit/CacheLoader.java
+++ b/core/java/android/webkit/CacheLoader.java
@@ -43,7 +43,7 @@
protected boolean setupStreamAndSendStatus() {
mDataStream = mCacheResult.inStream;
mContentLength = mCacheResult.contentLength;
- mHandler.status(1, 1, mCacheResult.httpStatusCode, "OK");
+ mLoadListener.status(1, 1, mCacheResult.httpStatusCode, "OK");
return true;
}
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 6790c5d..61a2d2ef 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -651,7 +651,42 @@
String message = msg.getData().getString("message");
String sourceID = msg.getData().getString("sourceID");
int lineNumber = msg.getData().getInt("lineNumber");
- mWebChromeClient.onConsoleMessage(message, lineNumber, sourceID);
+ int msgLevel = msg.getData().getInt("msgLevel");
+ int numberOfMessageLevels = ConsoleMessage.MessageLevel.values().length;
+ // Sanity bounds check as we'll index an array with msgLevel
+ if (msgLevel < 0 || msgLevel >= numberOfMessageLevels) {
+ msgLevel = 0;
+ }
+
+ ConsoleMessage.MessageLevel messageLevel =
+ ConsoleMessage.MessageLevel.values()[msgLevel];
+
+ if (!mWebChromeClient.onConsoleMessage(new ConsoleMessage(message, sourceID,
+ lineNumber, messageLevel))) {
+ // If false was returned the user did not provide their own console function so
+ // we should output some default messages to the system log.
+ String logTag = "Web Console";
+ String logMessage = message + " at " + sourceID + ":" + lineNumber;
+
+ switch (messageLevel) {
+ case TIP:
+ Log.v(logTag, logMessage);
+ break;
+ case LOG:
+ Log.i(logTag, logMessage);
+ break;
+ case WARNING:
+ Log.w(logTag, logMessage);
+ break;
+ case ERROR:
+ Log.e(logTag, logMessage);
+ break;
+ case DEBUG:
+ Log.d(logTag, logMessage);
+ break;
+ }
+ }
+
break;
case GET_VISITED_HISTORY:
@@ -1286,8 +1321,10 @@
* occurred.
* @param sourceID The filename of the source file in which the error
* occurred.
+ * @param msgLevel The message level, corresponding to the MessageLevel enum in
+ * WebCore/page/Console.h
*/
- public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+ public void addMessageToConsole(String message, int lineNumber, String sourceID, int msgLevel) {
if (mWebChromeClient == null) {
return;
}
@@ -1296,6 +1333,7 @@
msg.getData().putString("message", message);
msg.getData().putString("sourceID", sourceID);
msg.getData().putInt("lineNumber", lineNumber);
+ msg.getData().putInt("msgLevel", msgLevel);
sendMessage(msg);
}
diff --git a/core/java/android/webkit/ConsoleMessage.java b/core/java/android/webkit/ConsoleMessage.java
new file mode 100644
index 0000000..a9c351a
--- /dev/null
+++ b/core/java/android/webkit/ConsoleMessage.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * Public class representing a JavaScript console message from WebCore. This could be a issued
+ * by a call to one of the <code>console</code> logging functions (e.g.
+ * <code>console.log('...')</code>) or a JavaScript error on the page. To receive notifications
+ * of these messages, override the
+ * {@link WebChromeClient#onConsoleMessage(ConsoleMessage)} function.
+ */
+public class ConsoleMessage {
+
+ // This must be kept in sync with the WebCore enum in WebCore/page/Console.h
+ public enum MessageLevel {
+ TIP,
+ LOG,
+ WARNING,
+ ERROR,
+ DEBUG
+ };
+
+ private MessageLevel mLevel;
+ private String mMessage;
+ private String mSourceId;
+ private int mLineNumber;
+
+ public ConsoleMessage(String message, String sourceId, int lineNumber, MessageLevel msgLevel) {
+ mMessage = message;
+ mSourceId = sourceId;
+ mLineNumber = lineNumber;
+ mLevel = msgLevel;
+ }
+
+ public MessageLevel messageLevel() {
+ return mLevel;
+ }
+
+ public String message() {
+ return mMessage;
+ }
+
+ public String sourceId() {
+ return mSourceId;
+ }
+
+ public int lineNumber() {
+ return mLineNumber;
+ }
+};
diff --git a/core/java/android/webkit/ContentLoader.java b/core/java/android/webkit/ContentLoader.java
index 5eb54b0..d13210aa 100644
--- a/core/java/android/webkit/ContentLoader.java
+++ b/core/java/android/webkit/ContentLoader.java
@@ -16,14 +16,10 @@
package android.webkit;
-import android.content.Context;
import android.net.http.EventHandler;
import android.net.http.Headers;
import android.net.Uri;
-import java.io.File;
-import java.io.FileInputStream;
-
/**
* This class is a concrete implementation of StreamLoader that loads
* "content:" URIs
@@ -68,7 +64,7 @@
protected boolean setupStreamAndSendStatus() {
Uri uri = Uri.parse(mUrl);
if (uri == null) {
- mHandler.error(
+ mLoadListener.error(
EventHandler.FILE_NOT_FOUND_ERROR,
mContext.getString(
com.android.internal.R.string.httpErrorBadUrl) +
@@ -78,18 +74,14 @@
try {
mDataStream = mContext.getContentResolver().openInputStream(uri);
- mHandler.status(1, 1, 200, "OK");
+ mLoadListener.status(1, 1, 200, "OK");
} catch (java.io.FileNotFoundException ex) {
- mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
- return false;
-
- } catch (java.io.IOException ex) {
- mHandler.error(EventHandler.FILE_ERROR, errString(ex));
+ mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
return false;
} catch (RuntimeException ex) {
// readExceptionWithFileNotFoundExceptionFromParcel in DatabaseUtils
// can throw a serial of RuntimeException. Catch them all here.
- mHandler.error(EventHandler.FILE_ERROR, errString(ex));
+ mLoadListener.error(EventHandler.FILE_ERROR, errString(ex));
return false;
}
return true;
@@ -103,16 +95,4 @@
// content can change, we don't want WebKit to cache it
headers.setCacheControl("no-store, no-cache");
}
-
- /**
- * Construct a ContentLoader and instruct it to start loading.
- *
- * @param url "content:" url pointing to content to be loaded
- * @param loadListener LoadListener to pass the content to
- */
- public static void requestUrl(String url, LoadListener loadListener) {
- ContentLoader loader = new ContentLoader(url, loadListener);
- loader.load();
- }
-
}
diff --git a/core/java/android/webkit/DataLoader.java b/core/java/android/webkit/DataLoader.java
index 2a68a5d..235dc5be 100644
--- a/core/java/android/webkit/DataLoader.java
+++ b/core/java/android/webkit/DataLoader.java
@@ -62,10 +62,10 @@
@Override
protected boolean setupStreamAndSendStatus() {
if (mDataStream != null) {
- mHandler.status(1, 1, 200, "OK");
+ mLoadListener.status(1, 1, 200, "OK");
return true;
} else {
- mHandler.error(EventHandler.ERROR,
+ mLoadListener.error(EventHandler.ERROR,
mContext.getString(R.string.httpError));
return false;
}
@@ -74,16 +74,4 @@
@Override
protected void buildHeaders(android.net.http.Headers h) {
}
-
- /**
- * Construct a DataLoader and instruct it to start loading.
- *
- * @param url data: URL string optionally containing a mimetype
- * @param loadListener LoadListener to pass the content to
- */
- public static void requestUrl(String url, LoadListener loadListener) {
- DataLoader loader = new DataLoader(url, loadListener);
- loader.load();
- }
-
}
diff --git a/core/java/android/webkit/FileLoader.java b/core/java/android/webkit/FileLoader.java
index e856cde..e21e9ef 100644
--- a/core/java/android/webkit/FileLoader.java
+++ b/core/java/android/webkit/FileLoader.java
@@ -18,11 +18,9 @@
import com.android.internal.R;
-import android.content.Context;
import android.content.res.AssetManager;
import android.net.http.EventHandler;
import android.net.http.Headers;
-import android.os.Environment;
import android.util.Log;
import android.util.TypedValue;
@@ -111,7 +109,7 @@
// "<package>.R$drawable"
if (mPath == null || mPath.length() == 0) {
Log.e(LOGTAG, "Need a path to resolve the res file");
- mHandler.error(EventHandler.FILE_ERROR, mContext
+ mLoadListener.error(EventHandler.FILE_ERROR, mContext
.getString(R.string.httpErrorFileNotFound));
return false;
@@ -120,7 +118,7 @@
int dot = mPath.indexOf('.', slash);
if (slash == -1 || dot == -1) {
Log.e(LOGTAG, "Incorrect res path: " + mPath);
- mHandler.error(EventHandler.FILE_ERROR, mContext
+ mLoadListener.error(EventHandler.FILE_ERROR, mContext
.getString(R.string.httpErrorFileNotFound));
return false;
}
@@ -157,13 +155,13 @@
errorMsg = "Caught IllegalAccessException: " + e;
}
if (errorMsg != null) {
- mHandler.error(EventHandler.FILE_ERROR, mContext
+ mLoadListener.error(EventHandler.FILE_ERROR, mContext
.getString(R.string.httpErrorFileNotFound));
return false;
}
} else {
if (!mAllowFileAccess) {
- mHandler.error(EventHandler.FILE_ERROR,
+ mLoadListener.error(EventHandler.FILE_ERROR,
mContext.getString(R.string.httpErrorFileNotFound));
return false;
}
@@ -171,14 +169,14 @@
mDataStream = new FileInputStream(mPath);
mContentLength = (new File(mPath)).length();
}
- mHandler.status(1, 1, 200, "OK");
+ mLoadListener.status(1, 1, 200, "OK");
} catch (java.io.FileNotFoundException ex) {
- mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
+ mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
return false;
} catch (java.io.IOException ex) {
- mHandler.error(EventHandler.FILE_ERROR, errString(ex));
+ mLoadListener.error(EventHandler.FILE_ERROR, errString(ex));
return false;
}
return true;
@@ -188,22 +186,4 @@
protected void buildHeaders(Headers headers) {
// do nothing.
}
-
-
- /**
- * Construct a FileLoader and instruct it to start loading.
- *
- * @param url Full file url pointing to content to be loaded
- * @param loadListener LoadListener to pass the content to
- * @param asset true if url points to an asset.
- * @param allowFileAccess true if this FileLoader can load files from the
- * file system.
- */
- public static void requestUrl(String url, LoadListener loadListener,
- int type, boolean allowFileAccess) {
- FileLoader loader = new FileLoader(url, loadListener, type,
- allowFileAccess);
- loader.load();
- }
-
}
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 58eca38..b13c405 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -141,24 +141,29 @@
return true;
}
if (URLUtil.isAssetUrl(url)) {
- FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_ASSET,
- true);
+ // load asset in a separate thread as it involves IO
+ new FileLoader(url, loadListener, FileLoader.TYPE_ASSET, true)
+ .enqueue();
return true;
} else if (URLUtil.isResourceUrl(url)) {
- FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_RES,
- true);
+ // load resource in a separate thread as it involves IO
+ new FileLoader(url, loadListener, FileLoader.TYPE_RES, true)
+ .enqueue();
return true;
} else if (URLUtil.isFileUrl(url)) {
- FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_FILE,
- settings.getAllowFileAccess());
+ // load file in a separate thread as it involves IO
+ new FileLoader(url, loadListener, FileLoader.TYPE_FILE, settings
+ .getAllowFileAccess()).enqueue();
return true;
} else if (URLUtil.isContentUrl(url)) {
// Send the raw url to the ContentLoader because it will do a
- // permission check and the url has to match..
- ContentLoader.requestUrl(loadListener.url(), loadListener);
+ // permission check and the url has to match.
+ // load content in a separate thread as it involves IO
+ new ContentLoader(loadListener.url(), loadListener).enqueue();
return true;
} else if (URLUtil.isDataUrl(url)) {
- DataLoader.requestUrl(url, loadListener);
+ // load data in the current thread to reduce the latency
+ new DataLoader(url, loadListener).load();
return true;
} else if (URLUtil.isAboutUrl(url)) {
loadListener.data(mAboutBlank.getBytes(), mAboutBlank.length());
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
index ce26268..4c32997 100644
--- a/core/java/android/webkit/StreamLoader.java
+++ b/core/java/android/webkit/StreamLoader.java
@@ -20,12 +20,13 @@
import android.net.http.EventHandler;
import android.net.http.Headers;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Message;
import java.io.IOException;
import java.io.InputStream;
-
/**
* This abstract class is used for all content loaders that rely on streaming
* content into the rendering engine loading framework.
@@ -44,9 +45,7 @@
* that indicates the content should not be cached.
*
*/
-abstract class StreamLoader extends Handler {
-
- public static final String NO_STORE = "no-store";
+abstract class StreamLoader implements Handler.Callback {
private static final int MSG_STATUS = 100; // Send status to loader
private static final int MSG_HEADERS = 101; // Send headers to loader
@@ -54,11 +53,19 @@
private static final int MSG_END = 103; // Send endData to loader
protected final Context mContext;
- protected final LoadListener mHandler; // loader class
+ protected final LoadListener mLoadListener; // loader class
protected InputStream mDataStream; // stream to read data from
protected long mContentLength; // content length of data
private byte [] mData; // buffer to pass data to loader with.
+ // Handler which will be initialized in the thread where load() is called.
+ private Handler mHandler;
+
+ // Handler which will be used to load StreamLoader in a separate thread
+ private static StreamQueueHandler sStreamQueueHandler;
+
+ private static final Object sStreamQueueLock = new Object();
+
/**
* Constructor. Although this class calls the LoadListener, it only calls
* the EventHandler Interface methods. LoadListener concrete class is used
@@ -67,13 +74,13 @@
* @param loadlistener The LoadListener to call with the data.
*/
StreamLoader(LoadListener loadlistener) {
- mHandler = loadlistener;
+ mLoadListener = loadlistener;
mContext = loadlistener.getContext();
}
/**
* This method is called when the derived class should setup mDataStream,
- * and call mHandler.status() to indicate that the load can occur. If it
+ * and call mLoadListener.status() to indicate that the load can occur. If it
* fails to setup, it should still call status() with the error code.
*
* @return true if stream was successfully setup
@@ -89,15 +96,40 @@
*/
abstract protected void buildHeaders(Headers headers);
+ /**
+ * Calling this method to load this StreamLoader in a separate
+ * "StreamLoadingThread".
+ */
+ final void enqueue() {
+ synchronized (sStreamQueueLock) {
+ if (sStreamQueueHandler == null) {
+ HandlerThread thread = new HandlerThread(
+ StreamQueueHandler.THREAD_NAME,
+ android.os.Process.THREAD_PRIORITY_DEFAULT +
+ android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
+ thread.start();
+ sStreamQueueHandler = new StreamQueueHandler(thread.getLooper());
+ }
+ }
+
+ sStreamQueueHandler.obtainMessage(StreamQueueHandler.MSG_ADD_LOADER,
+ this).sendToTarget();
+ }
/**
* Calling this method starts the load of the content for this StreamLoader.
- * This method simply posts a message to send the status and returns
- * immediately.
+ * This method simply creates a Handler in the current thread and posts a
+ * message to send the status and returns immediately.
*/
- public void load() {
- if (!mHandler.isSynchronous()) {
- sendMessage(obtainMessage(MSG_STATUS));
+ final void load() {
+ synchronized (this) {
+ if (mHandler == null) {
+ mHandler = new Handler(this);
+ }
+ }
+
+ if (!mLoadListener.isSynchronous()) {
+ mHandler.sendEmptyMessage(MSG_STATUS);
} else {
// Load the stream synchronously.
if (setupStreamAndSendStatus()) {
@@ -105,23 +137,20 @@
// to pass data to the loader
mData = new byte[8192];
sendHeaders();
- while (!sendData() && !mHandler.cancelled());
+ while (!sendData() && !mLoadListener.cancelled());
closeStreamAndSendEndData();
- mHandler.loadSynchronousMessages();
+ mLoadListener.loadSynchronousMessages();
}
}
}
- /* (non-Javadoc)
- * @see android.os.Handler#handleMessage(android.os.Message)
- */
- public void handleMessage(Message msg) {
- if (DebugFlags.STREAM_LOADER && mHandler.isSynchronous()) {
+ public boolean handleMessage(Message msg) {
+ if (mLoadListener.isSynchronous()) {
throw new AssertionError();
}
- if (mHandler.cancelled()) {
+ if (mLoadListener.cancelled()) {
closeStreamAndSendEndData();
- return;
+ return true;
}
switch(msg.what) {
case MSG_STATUS:
@@ -129,27 +158,27 @@
// We were able to open the stream, create the array
// to pass data to the loader
mData = new byte[8192];
- sendMessage(obtainMessage(MSG_HEADERS));
+ mHandler.sendEmptyMessage(MSG_HEADERS);
}
break;
case MSG_HEADERS:
sendHeaders();
- sendMessage(obtainMessage(MSG_DATA));
+ mHandler.sendEmptyMessage(MSG_DATA);
break;
case MSG_DATA:
if (sendData()) {
- sendMessage(obtainMessage(MSG_END));
+ mHandler.sendEmptyMessage(MSG_END);
} else {
- sendMessage(obtainMessage(MSG_DATA));
+ mHandler.sendEmptyMessage(MSG_DATA);
}
break;
case MSG_END:
closeStreamAndSendEndData();
break;
default:
- super.handleMessage(msg);
- break;
+ return false;
}
+ return true;
}
/**
@@ -161,7 +190,7 @@
headers.setContentLength(mContentLength);
}
buildHeaders(headers);
- mHandler.headers(headers);
+ mLoadListener.headers(headers);
}
/**
@@ -176,12 +205,11 @@
try {
int amount = mDataStream.read(mData);
if (amount > 0) {
- mHandler.data(mData, amount);
+ mLoadListener.data(mData, amount);
return false;
}
} catch (IOException ex) {
- mHandler.error(EventHandler.FILE_ERROR,
- ex.getMessage());
+ mLoadListener.error(EventHandler.FILE_ERROR, ex.getMessage());
}
}
return true;
@@ -198,7 +226,24 @@
// ignore.
}
}
- mHandler.endData();
+ mLoadListener.endData();
}
+ private static class StreamQueueHandler extends Handler {
+ private static final String THREAD_NAME = "StreamLoadingThread";
+
+ private static final int MSG_ADD_LOADER = 101;
+
+ StreamQueueHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_ADD_LOADER) {
+ StreamLoader loader = (StreamLoader) msg.obj;
+ loader.load();
+ }
+ }
+ }
}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index f40b55c..1d5aac7 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -262,8 +262,24 @@
* @param message The error message to report.
* @param lineNumber The line number of the error.
* @param sourceID The name of the source file that caused the error.
+ * @deprecated Use {@link #onConsoleMessage(ConsoleMessage) onConsoleMessage(ConsoleMessage)}
+ * instead.
*/
- public void onConsoleMessage(String message, int lineNumber, String sourceID) {}
+ @Deprecated
+ public void onConsoleMessage(String message, int lineNumber, String sourceID) { }
+
+ /**
+ * Report a JavaScript console message to the host application. The ChromeClient
+ * should override this to process the log message as they see fit.
+ * @param consoleMessage Object containing details of the console message.
+ * @return true if the message is handled by the client.
+ */
+ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+ // Call the old version of this function for backwards compatability.
+ onConsoleMessage(consoleMessage.message(), consoleMessage.lineNumber(),
+ consoleMessage.sourceId());
+ return false;
+ }
/**
* When not playing, video elements are represented by a 'poster' image. The
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 6e45e39..9c91919 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -260,9 +260,12 @@
* @param message The message to add
* @param lineNumber the line on which the error occurred
* @param sourceID the filename of the source that caused the error.
+ * @param msgLevel the log level of this message. This is a value casted to int
+ * from WebCore::MessageLevel in WebCore/page/Console.h.
*/
- protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
- mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
+ protected void addMessageToConsole(String message, int lineNumber, String sourceID,
+ int msgLevel) {
+ mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID, msgLevel);
}
/**
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
new file mode 100644
index 0000000..2b93dbc
--- /dev/null
+++ b/core/java/com/android/internal/app/TetherActivity.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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.app;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IMountService;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.widget.Toast;
+import android.util.Log;
+
+/**
+ * This activity is shown to the user for him/her to connect/disconnect a Tether
+ * connection. It will display notification when a suitable connection is made
+ * to allow the tether to be setup. A second notification will be show when a
+ * tether is active, allowing the user to manage tethered connections.
+ */
+public class TetherActivity extends AlertActivity implements
+ DialogInterface.OnClickListener {
+
+ private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
+
+ /* Used to detect when the USB cable is unplugged, so we can call finish() */
+ private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction() == ConnectivityManager.ACTION_TETHER_STATE_CHANGED) {
+ handleTetherStateChanged(intent);
+ }
+ }
+ };
+
+ private boolean mWantTethering;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // determine if we advertise tethering or untethering
+ ConnectivityManager cm =
+ (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (cm.getTetheredIfaces().length > 0) {
+ mWantTethering = false;
+ } else if (cm.getTetherableIfaces().length > 0) {
+ mWantTethering = true;
+ } else {
+ finish();
+ return;
+ }
+
+ // Set up the "dialog"
+ if (mWantTethering == true) {
+ mAlertParams.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
+ mAlertParams.mTitle = getString(com.android.internal.R.string.tether_title);
+ mAlertParams.mMessage = getString(com.android.internal.R.string.tether_message);
+ mAlertParams.mPositiveButtonText =
+ getString(com.android.internal.R.string.tether_button);
+ mAlertParams.mPositiveButtonListener = this;
+ mAlertParams.mNegativeButtonText =
+ getString(com.android.internal.R.string.tether_button_cancel);
+ mAlertParams.mNegativeButtonListener = this;
+ } else {
+ mAlertParams.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
+ mAlertParams.mTitle = getString(com.android.internal.R.string.tether_stop_title);
+ mAlertParams.mMessage = getString(com.android.internal.R.string.tether_stop_message);
+ mAlertParams.mPositiveButtonText =
+ getString(com.android.internal.R.string.tether_stop_button);
+ mAlertParams.mPositiveButtonListener = this;
+ mAlertParams.mNegativeButtonText =
+ getString(com.android.internal.R.string.tether_stop_button_cancel);
+ mAlertParams.mNegativeButtonListener = this;
+ }
+ setupAlert();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ registerReceiver(mTetherReceiver, new IntentFilter(
+ ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ unregisterReceiver(mTetherReceiver);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onClick(DialogInterface dialog, int which) {
+
+ if (which == POSITIVE_BUTTON) {
+ ConnectivityManager connManager =
+ (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+ // start/stop tethering
+ if (mWantTethering) {
+ if (!connManager.tether("ppp0")) {
+ showTetheringError();
+ }
+ } else {
+ if (!connManager.untether("ppp0")) {
+ showUnTetheringError();
+ }
+ }
+ }
+ // No matter what, finish the activity
+ finish();
+ }
+
+ private void handleTetherStateChanged(Intent intent) {
+ finish();
+ }
+
+ private void showTetheringError() {
+ Toast.makeText(this, com.android.internal.R.string.tether_error_message,
+ Toast.LENGTH_LONG).show();
+ }
+
+ private void showUnTetheringError() {
+ Toast.makeText(this, com.android.internal.R.string.tether_stop_error_message,
+ Toast.LENGTH_LONG).show();
+ }
+
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 665088a..1406b66 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1239,6 +1239,10 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+ <activity android:name="com.android.internal.app.TetherActivity"
+ android:theme="@style/Theme.Dialog.Alert"
+ android:excludeFromRecents="true">
+ </activity>
<activity android:name="com.android.internal.app.UsbStorageActivity"
android:excludeFromRecents="true">
</activity>
diff --git a/core/res/res/drawable-hdpi/search_source_selector_indicator.png b/core/res/res/drawable-hdpi/search_source_selector_indicator.png
deleted file mode 100644
index b93a0c0..0000000
--- a/core/res/res/drawable-hdpi/search_source_selector_indicator.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_active.png b/core/res/res/drawable-hdpi/stat_sys_tether_active.png
new file mode 100755
index 0000000..4c14c07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_active.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_usb.png b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png
new file mode 100755
index 0000000..4c14c07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android.png b/core/res/res/drawable-hdpi/usb_android.png
index 8153ec4..f6f899a 100644
--- a/core/res/res/drawable-hdpi/usb_android.png
+++ b/core/res/res/drawable-hdpi/usb_android.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android_connected.png b/core/res/res/drawable-hdpi/usb_android_connected.png
index 6449b7c..583ca00 100644
--- a/core/res/res/drawable-hdpi/usb_android_connected.png
+++ b/core/res/res/drawable-hdpi/usb_android_connected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/search_source_selector_indicator.png b/core/res/res/drawable-mdpi/search_source_selector_indicator.png
deleted file mode 100644
index 26bf18a..0000000
--- a/core/res/res/drawable-mdpi/search_source_selector_indicator.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_active.png b/core/res/res/drawable-mdpi/stat_sys_tether_active.png
new file mode 100644
index 0000000..2d0da4c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_active.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_usb.png b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png
new file mode 100644
index 0000000..2d0da4c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/usb_android.png b/core/res/res/drawable-mdpi/usb_android.png
new file mode 100644
index 0000000..df1afbb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/usb_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/usb_android_connected.png b/core/res/res/drawable-mdpi/usb_android_connected.png
new file mode 100644
index 0000000..fca77a7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/usb_android_connected.png
Binary files differ
diff --git a/core/res/res/drawable/search_source_selector_background.xml b/core/res/res/drawable/search_source_selector_background.xml
deleted file mode 100644
index fcacd89..0000000
--- a/core/res/res/drawable/search_source_selector_background.xml
+++ /dev/null
@@ -1,21 +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">
-
- <!-- TODO: Need focused and pressed backgrounds -->
-
-</selector>
diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml
index 12285fd..cf246ba 100644
--- a/core/res/res/layout/search_bar.xml
+++ b/core/res/res/layout/search_bar.xml
@@ -55,11 +55,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
-
- <include android:id="@+id/search_source_selector"
- layout="@layout/search_source_selector"
+
+ <ImageView
+ android:id="@+id/search_app_icon"
+ android:layout_height="36dip"
+ android:layout_width="36dip"
android:layout_marginRight="7dip"
- android:layout_gravity="center_vertical" />
+ android:layout_gravity="center_vertical"
+ />
<view class="android.app.SearchDialog$SearchAutoComplete"
android:id="@+id/search_src_text"
diff --git a/core/res/res/layout/search_source_selector.xml b/core/res/res/layout/search_source_selector.xml
deleted file mode 100644
index c69dfc0..0000000
--- a/core/res/res/layout/search_source_selector.xml
+++ /dev/null
@@ -1,35 +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.
--->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="48dip"
- android:layout_height="match_parent"
- android:foreground="@drawable/search_source_selector_indicator"
- android:foregroundGravity="bottom|right"
- >
-
- <ImageButton
- android:id="@+id/search_source_selector_icon"
- android:background="@drawable/search_source_selector_background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:focusable="true"
- android:clickable="true"
- />
-
-</FrameLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2b8ddc4..46d6352 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -237,4 +237,8 @@
<!-- Component name of the service providing geocoder API support. -->
<string name="config_geocodeProvider">@null</string>
+
+ <!-- Flag indicating whether headset events are used by kernel to indicate
+ TTY mode changes. -->
+ <bool name="tty_mode_uses_headset_events">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 30d0da7..d1bfc68 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1078,7 +1078,13 @@
<string name="permlab_changeNetworkState">change network connectivity</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_changeNetworkState">Allows an application to change
- the state network connectivity.</string>
+ the state of network connectivity.</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_changeTetherState">change tethered connectivity</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the applicaiton to do this. -->
+ <string name="permdesc_changeTetherState">Allows an application to change
+ the state of tethered network connectivity.</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_changeBackgroundDataSetting">change background data usage setting</string>
@@ -2200,4 +2206,40 @@
Used by AccessibilityService to announce the purpose of the view.
-->
<string name="description_star">favorite</string>
+
+
+ <!-- Strings for Tethering dialogs -->
+ <!-- This is the label for the activity, and should never be visible to the user. -->
+ <!-- See TETHERING. TETHERING_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to Tether. This is the title. -->
+ <string name="tether_title">USB tethering available</string>
+ <!-- See TETHER. This is the message. -->
+ <string name="tether_message">Select \"Tether\" if you want to share your phone\'s data connection with your computer.</string>
+ <!-- See TETHER. This is the button text to Tether the computer with the phone. -->
+ <string name="tether_button">Tether</string>
+ <!-- See TETHER. This is the button text to ignore the plugging in of the phone.. -->
+ <string name="tether_button_cancel">Cancel</string>
+ <!-- See TETHER. If there was an error mounting, this is the text. -->
+ <string name="tether_error_message">There is a problem tethering.</string>
+ <!-- TETHER: When the user connects the phone to a computer, we show a notification asking if he wants to share his cellular network connection. This is the title -->
+ <string name="tether_available_notification_title">USB tethering available</string>
+ <!-- See USB_STORAGE. This is the message. -->
+ <string name="tether_available_notification_message">Select to tether your computer to your phone.</string>
+ <!-- TETHER_STOP: While TETHER is enabled, we show a notification dialog asking if he wants to stop. This is the title -->
+ <string name="tether_stop_notification_title">Untether</string>
+ <!-- See TETHER. This is the message. -->
+ <string name="tether_stop_notification_message">Select to untether your computer.</string>
+
+ <!-- TETHER stop dialog strings -->
+ <!-- This is the label for the activity, and should never be visible to the user. -->
+ <!-- See TETHER_STOP. TETHER_STOP_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to stop tethering. This is the title. -->
+ <string name="tether_stop_title">Disconnect tethering</string>
+ <!-- See TETHER_STOP. This is the message. -->
+ <string name="tether_stop_message">You have been sharing your phone\'s cellular data connection with your computer. Select \"Disconnect\" to disconnect USB tethering.</string>
+ <!-- See TETHER_STOP. This is the button text to disconnect tethering. -->
+ <string name="tether_stop_button">Disconnect</string>
+ <!-- See TETHER_STOP. This is the button text to cancel disconnecting the tether. -->
+ <string name="tether_stop_button_cancel">Cancel</string>
+ <!-- See TETHER_STOP_DIALOG. If there was an error disconnect, this is the text. -->
+ <string name="tether_stop_error_message">We\'ve encountered a problem turning off Tethering. Please try again.</string>
+
</resources>
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index be928ec..3f253f9 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -53,8 +53,8 @@
*
* Supported param name are:
* file.format - output file format. see mediarecorder.h for details
- * codec.vid - video encoder. see mediarecorder.h for details.
- * codec.aud - audio encoder. see mediarecorder.h for details.
+ * vid.codec - video encoder. see mediarecorder.h for details.
+ * aud.codec - audio encoder. see mediarecorder.h for details.
* vid.width - video frame width
* vid.height - video frame height
* vid.fps - video frame rate
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
new file mode 100644
index 0000000..ce56443
--- /dev/null
+++ b/media/java/android/media/CamcorderProfile.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The CamcorderProfile class is used to retrieve the
+ * predefined camcorder profile settings for camcorder applications.
+ * The compressed output from a recording session with a given
+ * CamcorderProfile contains two tracks: one for auido and one for video.
+ *
+ * <p>Each profile specifies the following set of parameters:
+ * <ul>
+ * <li> The file output format, @see android.media.MediaRecorder.OutputFormat
+ * <li> Video codec format, @see android.media.MediaRecorder.VideoEncoder
+ * <li> Video bit rate in bits per second
+ * <li> Video frame rate in frames per second
+ * <li> Video frame width and height,
+ * <li> Audio codec format, @see android.media.MediaRecorder.AudioEncoder
+ * <li> Audio bit rate in bits per second,
+ * <li> Audio sample rate
+ * <li> Number of audio channels for recording.
+ * </ul>
+ * {@hide}
+ */
+public class CamcorderProfile
+{
+
+ /**
+ * The Quality class represents the quality level of each CamcorderProfile.
+ *
+ * The output from recording sessions with high quality level usually may have
+ * larger output bit rate, better video and/or audio recording quality, and
+ * laerger video frame resolution and higher audio sampling rate, etc, than those
+ * with low quality level.
+ */
+ public enum Quality {
+ /* Do not change these values/ordinals without updating their counterpart
+ * in include/media/MediaProfiles.h!
+ */
+ HIGH,
+ LOW
+ };
+
+ /**
+ * The quality level of the camcorder profile
+ * @see android.media.CamcorderProfile.Quality
+ */
+ public final Quality mQuality;
+
+ /**
+ * The file output format of the camcorder profile
+ * @see android.media.MediaRecorder.OutputFormat
+ */
+ public final int mFileFormat;
+
+ /**
+ * The video encoder being used for the video track
+ * @see android.media.MediaRecorder.VideoEncoder
+ */
+ public final int mVideoCodec;
+
+ /**
+ * The target video output bit rate in bits per second
+ */
+ public final int mVideoBitRate;
+
+ /**
+ * The target video frame rate in frames per second
+ */
+ public final int mVideoFrameRate;
+
+ /**
+ * The target video frame width in pixels
+ */
+ public final int mVideoFrameWidth;
+
+ /**
+ * The target video frame height in pixels
+ */
+ public final int mVideoFrameHeight;
+
+ /**
+ * The audio encoder being used for the audio track.
+ * @see android.media.MediaRecorder.AudioEncoder
+ */
+ public final int mAudioCodec;
+
+ /**
+ * The target audio output bit rate in bits per second
+ */
+ public final int mAudioBitRate;
+
+ /**
+ * The audio sampling rate used for the audio track
+ */
+ public final int mAudioSampleRate;
+
+ /**
+ * The number of audio channels used for the audio track
+ */
+ public final int mAudioChannels;
+
+ /**
+ * Returns the camcorder profile for the given quality.
+ * @param quality the target quality level for the camcorder profile
+ * @see android.media.CamcorderProfile.Quality
+ */
+ public static CamcorderProfile get(Quality quality) {
+ return native_get_camcorder_profile(quality.ordinal());
+ }
+
+ static {
+ System.loadLibrary("media_jni");
+ native_init();
+ }
+
+ // Private constructor called by JNI
+ private CamcorderProfile(int quality,
+ int fileFormat,
+ int videoCodec,
+ int videoBitRate,
+ int videoFrameRate,
+ int videoWidth,
+ int videoHeight,
+ int audioCodec,
+ int audioBitRate,
+ int audioSampleRate,
+ int audioChannels) {
+
+ mQuality = Quality.values()[quality];
+ mFileFormat = fileFormat;
+ mVideoCodec = videoCodec;
+ mVideoBitRate = videoBitRate;
+ mVideoFrameRate = videoFrameRate;
+ mVideoFrameWidth = videoWidth;
+ mVideoFrameHeight = videoHeight;
+ mAudioCodec = audioCodec;
+ mAudioBitRate = audioBitRate;
+ mAudioSampleRate = audioSampleRate;
+ mAudioChannels = audioChannels;
+ }
+
+ // Methods implemented by JNI
+ private static native final void native_init();
+ private static native final CamcorderProfile native_get_camcorder_profile(int quality);
+}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index e9d3372..4292754 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -28,22 +28,37 @@
* This is a class for reading and writing Exif tags in a JPEG file.
*/
public class ExifInterface {
-
// The Exif tag names
+ /** Type is int. */
public static final String TAG_ORIENTATION = "Orientation";
+ /** Type is String. */
public static final String TAG_DATETIME = "DateTime";
+ /** Type is String. */
public static final String TAG_MAKE = "Make";
+ /** Type is String. */
public static final String TAG_MODEL = "Model";
+ /** Type is int. */
public static final String TAG_FLASH = "Flash";
+ /** Type is int. */
public static final String TAG_IMAGE_WIDTH = "ImageWidth";
+ /** Type is int. */
public static final String TAG_IMAGE_LENGTH = "ImageLength";
+ /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
public static final String TAG_GPS_LATITUDE = "GPSLatitude";
+ /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
+ /** Type is String. */
public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+ /** Type is String. */
public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ /** Type is String. */
public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ /** Type is String. */
public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
+ /** Type is int. */
public static final String TAG_WHITE_BALANCE = "WhiteBalance";
+ /** Type is rational. */
+ public static final String TAG_FOCAL_LENGTH = "FocalLength";
// Constants used for the Orientation Exif tag.
public static final int ORIENTATION_UNDEFINED = 0;
@@ -114,6 +129,29 @@
}
/**
+ * Returns the double value of the specified rational tag. If there is no
+ * such tag in the JPEG file or the value cannot be parsed as double, return
+ * <var>defaultValue</var>.
+ *
+ * @param tag the name of the tag.
+ * @param defaultValue the value to return if the tag is not available.
+ */
+ public double getAttributeDouble(String tag, double defaultValue) {
+ String value = mAttributes.get(tag);
+ if (value == null) return defaultValue;
+ try {
+ int index = value.indexOf("/");
+ if (index == -1) return defaultValue;
+ double denom = Double.parseDouble(value.substring(index + 1));
+ if (denom == 0) return defaultValue;
+ double num = Double.parseDouble(value.substring(0, index));
+ return num / denom;
+ } catch (NumberFormatException ex) {
+ return defaultValue;
+ }
+ }
+
+ /**
* Set the value of the specified tag.
*
* @param tag the name of the tag.
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index cd3ad88..50380c1 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -162,7 +162,54 @@
return cap;
}
-static JNINativeMethod gMethods[] = {
+static jobject
+android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint quality)
+{
+ LOGV("native_get_camcorder_profile: %d", quality);
+ if (quality != CAMCORDER_QUALITY_HIGH && quality != CAMCORDER_QUALITY_LOW) {
+ jniThrowException(env, "java/lang/RuntimeException", "Unknown camcorder profile quality");
+ return NULL;
+ }
+
+ camcorder_quality q = static_cast<camcorder_quality>(quality);
+ int fileFormat = sProfiles->getCamcorderProfileParamByName("file.format", q);
+ int videoCodec = sProfiles->getCamcorderProfileParamByName("vid.codec", q);
+ int videoBitRate = sProfiles->getCamcorderProfileParamByName("vid.bps", q);
+ int videoFrameRate = sProfiles->getCamcorderProfileParamByName("vid.fps", q);
+ int videoFrameWidth = sProfiles->getCamcorderProfileParamByName("vid.width", q);
+ int videoFrameHeight = sProfiles->getCamcorderProfileParamByName("vid.height", q);
+ int audioCodec = sProfiles->getCamcorderProfileParamByName("aud.codec", q);
+ int audioBitRate = sProfiles->getCamcorderProfileParamByName("aud.bps", q);
+ int audioSampleRate = sProfiles->getCamcorderProfileParamByName("aud.hz", q);
+ int audioChannels = sProfiles->getCamcorderProfileParamByName("aud.ch", q);
+
+ // Check on the values retrieved
+ if (fileFormat == -1 || videoCodec == -1 || audioCodec == -1 ||
+ videoBitRate == -1 || videoFrameRate == -1 || videoFrameWidth == -1 || videoFrameHeight == -1 ||
+ audioBitRate == -1 || audioSampleRate == -1 || audioChannels == -1) {
+
+ jniThrowException(env, "java/lang/RuntimeException", "Error retrieving camcorder profile params");
+ return NULL;
+ }
+
+ jclass camcorderProfileClazz = env->FindClass("android/media/CamcorderProfile");
+ jmethodID camcorderProfileConstructorMethodID = env->GetMethodID(camcorderProfileClazz, "<init>", "(IIIIIIIIIII)V");
+ return env->NewObject(camcorderProfileClazz,
+ camcorderProfileConstructorMethodID,
+ quality,
+ fileFormat,
+ videoCodec,
+ videoBitRate,
+ videoFrameRate,
+ videoFrameWidth,
+ videoFrameHeight,
+ audioCodec,
+ audioBitRate,
+ audioSampleRate,
+ audioChannels);
+}
+
+static JNINativeMethod gMethodsForEncoderCapabilitiesClass[] = {
{"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
{"native_get_num_file_formats", "()I", (void *)android_media_MediaProfiles_native_get_num_file_formats},
{"native_get_file_format", "(I)I", (void *)android_media_MediaProfiles_native_get_file_format},
@@ -176,12 +223,29 @@
(void *)android_media_MediaProfiles_native_get_audio_encoder_cap},
};
-static const char* const kClassPathName = "android/media/MediaProfiles";
+static JNINativeMethod gMethodsForCamcorderProfileClass[] = {
+ {"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
+ {"native_get_camcorder_profile", "(I)Landroid/media/CamcorderProfile;",
+ (void *)android_media_MediaProfiles_native_get_camcorder_profile},
+};
+
+static const char* const kEncoderCapabilitiesClassPathName = "android/media/EncoderCapabilities";
+static const char* const kCamcorderProfileClassPathName = "android/media/CamcorderProfile";
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaProfiles(JNIEnv *env)
{
- return AndroidRuntime::registerNativeMethods(env,
- "android/media/EncoderCapabilities", gMethods, NELEM(gMethods));
+ int ret1 = AndroidRuntime::registerNativeMethods(env,
+ kEncoderCapabilitiesClassPathName,
+ gMethodsForEncoderCapabilitiesClass,
+ NELEM(gMethodsForEncoderCapabilitiesClass));
+
+ int ret2 = AndroidRuntime::registerNativeMethods(env,
+ kCamcorderProfileClassPathName,
+ gMethodsForCamcorderProfileClass,
+ NELEM(gMethodsForCamcorderProfileClass));
+
+ // Success if ret1 == 0 && ret2 == 0
+ return (ret1 || ret2);
}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index dbb52c6..b25ce67 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -60,8 +60,6 @@
libsonivox \
libvorbisidec
-ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
-
LOCAL_STATIC_LIBRARIES := \
libstagefright_aacdec \
libstagefright_amrnbdec \
@@ -69,12 +67,18 @@
libstagefright_amrwbdec \
libstagefright_avcdec \
libstagefright_m4vh263dec \
- libstagefright_mp3dec \
- libstagefright_id3
+ libstagefright_mp3dec
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
- libstagefright_avc_common \
+ libstagefright_avc_common
+
+ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
+
+LOCAL_STATIC_LIBRARIES += \
+ libstagefright_id3
+
+LOCAL_SHARED_LIBRARIES += \
libstagefright_color_conversion
endif
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 90bbdfe..0355a82 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -18,7 +18,6 @@
#define LOG_TAG "OMXCodec"
#include <utils/Log.h>
-#if BUILD_WITH_FULL_STAGEFRIGHT
#include "include/AACDecoder.h"
#include "include/AMRNBDecoder.h"
#include "include/AMRNBEncoder.h"
@@ -26,7 +25,6 @@
#include "include/AVCDecoder.h"
#include "include/M4vH263Decoder.h"
#include "include/MP3Decoder.h"
-#endif
#include "include/ESDS.h"
@@ -56,9 +54,6 @@
const char *codec;
};
-#if BUILD_WITH_FULL_STAGEFRIGHT
-#define OPTIONAL(x,y) { x, y },
-
#define FACTORY_CREATE(name) \
static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \
return new name(source); \
@@ -103,42 +98,30 @@
#undef FACTORY_REF
#undef FACTORY_CREATE
-#else
-#define OPTIONAL(x,y)
-#endif
-
static const CodecInfo kDecoderInfo[] = {
{ MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
{ MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder")
- { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.PV.mp3dec" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder")
- { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrdec" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder")
- { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.PV.amrdec" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder")
- { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
+ { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
- OPTIONAL(MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder")
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.Decoder" },
- OPTIONAL(MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder")
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },
+ { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
- OPTIONAL(MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder")
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
};
static const CodecInfo kEncoderInfo[] = {
{ MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.encode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBEncoder")
- { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrencnb" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBEncoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.encode" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacenc" },
diff --git a/media/libstagefright/codecs/Android.mk b/media/libstagefright/codecs/Android.mk
index 1fa7e93..2e431205 100644
--- a/media/libstagefright/codecs/Android.mk
+++ b/media/libstagefright/codecs/Android.mk
@@ -1,8 +1,4 @@
-ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
-
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
include $(call all-makefiles-under,$(LOCAL_PATH))
-
-endif
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index 3c47e2e..add8f3c 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -1,3 +1,5 @@
+ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -25,3 +27,4 @@
include $(BUILD_EXECUTABLE)
+endif
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
new file mode 100644
index 0000000..b7ae4a9
--- /dev/null
+++ b/native/graphics/jni/Android.mk
@@ -0,0 +1,36 @@
+BASE_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PRELINK_MODULE := false
+
+# setup for skia optimizations
+#
+ifneq ($(ARCH_ARM_HAVE_VFP),true)
+ LOCAL_CFLAGS += -DSK_SOFTWARE_FLOAT
+endif
+
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+ LOCAL_CFLAGS += -D__ARM_HAVE_NEON
+endif
+
+# our source files
+#
+LOCAL_SRC_FILES:= \
+ bitmap.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid_runtime \
+ libskia
+
+LOCAL_C_INCLUDES += \
+ external/skia/include/core \
+ frameworks/base/native/include \
+ frameworks/base/core/jni/android/graphics \
+ dalvik/libnativehelper/include/nativehelper
+
+LOCAL_MODULE:= libjnigraphics
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
new file mode 100644
index 0000000..fd73430
--- /dev/null
+++ b/native/graphics/jni/bitmap.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <GraphicsJNI.h>
+
+int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
+ AndroidBitmapInfo* info) {
+ if (NULL == env || NULL == jbitmap) {
+ return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+ }
+
+ SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);
+ if (NULL == bm) {
+ return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+ }
+
+ if (info) {
+ info->width = bm->width();
+ info->height = bm->height();
+ info->stride = bm->rowBytes();
+ info->flags = 0;
+
+ switch (bm->config()) {
+ case SkBitmap::kARGB_8888_Config:
+ info->format = ANDROID_BITMAP_FORMAT_RGBA_8888;
+ break;
+ case SkBitmap::kRGB_565_Config:
+ info->format = ANDROID_BITMAP_FORMAT_RGB_565;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ info->format = ANDROID_BITMAP_FORMAT_RGBA_4444;
+ break;
+ case SkBitmap::kA8_Config:
+ info->format = ANDROID_BITMAP_FORMAT_A_8;
+ break;
+ default:
+ info->format = ANDROID_BITMAP_FORMAT_NONE;
+ break;
+ }
+ }
+ return ANDROID_BITMAP_RESUT_SUCCESS;
+}
+
+int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) {
+ if (NULL == env || NULL == jbitmap) {
+ return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+ }
+
+ SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);
+ if (NULL == bm) {
+ return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+ }
+
+ bm->lockPixels();
+ void* addr = bm->getPixels();
+ if (NULL == addr) {
+ bm->unlockPixels();
+ return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED;
+ }
+
+ if (addrPtr) {
+ *addrPtr = addr;
+ }
+ return ANDROID_BITMAP_RESUT_SUCCESS;
+}
+
+int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap) {
+ if (NULL == env || NULL == jbitmap) {
+ return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+ }
+
+ SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);
+ if (NULL == bm) {
+ return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+ }
+
+ bm->unlockPixels();
+ return ANDROID_BITMAP_RESUT_SUCCESS;
+}
+
diff --git a/native/include/android/bitmap.h b/native/include/android/bitmap.h
new file mode 100644
index 0000000..5078277
--- /dev/null
+++ b/native/include/android/bitmap.h
@@ -0,0 +1,79 @@
+/*
+ * 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 ANDROID_BITMAP_H
+#define ANDROID_BITMAP_H
+
+#include <stdint.h>
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ANDROID_BITMAP_RESUT_SUCCESS 0
+#define ANDROID_BITMAP_RESULT_BAD_PARAMETER -1
+#define ANDROID_BITMAP_RESULT_JNI_EXCEPTION -2
+#define ANDROID_BITMAP_RESULT_ALLOCATION_FAILED -3
+
+enum AndroidBitmapFormat {
+ ANDROID_BITMAP_FORMAT_NONE = 0,
+ ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,
+ ANDROID_BITMAP_FORMAT_RGB_565 = 4,
+ ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
+ ANDROID_BITMAP_FORMAT_A_8 = 8,
+};
+
+typedef struct {
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ int32_t format;
+ uint32_t flags; // 0 for now
+} AndroidBitmapInfo;
+
+/**
+ * Given a java bitmap object, fill out the AndroidBitmap struct for it.
+ * If the call fails, the info parameter will be ignored
+ */
+int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
+ AndroidBitmapInfo* info);
+
+/**
+ * Given a java bitmap object, attempt to lock the pixel address.
+ * Locking will ensure that the memory for the pixels will not move
+ * until the unlockPixels call, and ensure that, if the pixels had been
+ * previously purged, they will have been restored.
+ *
+ * If this call succeeds, it must be balanced by a call to
+ * AndroidBitmap_unlockPixels, after which time the address of the pixels should
+ * no longer be used.
+ *
+ * If this succeeds, *addrPtr will be set to the pixel address. If the call
+ * fails, addrPtr will be ignored.
+ */
+int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);
+
+/**
+ * Call this to balanace a successful call to AndroidBitmap_lockPixels
+ */
+int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/preloaded-classes b/preloaded-classes
index 3e2eb13..092b539 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -22,14 +22,14 @@
android.app.ActivityThread$ProviderRefCount
android.app.AlertDialog
android.app.Application
-android.app.ApplicationContext
-android.app.ApplicationContext$ApplicationContentResolver
-android.app.ApplicationContext$ApplicationPackageManager
-android.app.ApplicationContext$ApplicationPackageManager$PackageRemovedReceiver
-android.app.ApplicationContext$ApplicationPackageManager$ResourceName
-android.app.ApplicationContext$SharedPreferencesImpl
android.app.ApplicationLoaders
android.app.ApplicationThreadNative
+android.app.ContextImpl
+android.app.ContextImpl$ApplicationContentResolver
+android.app.ContextImpl$ApplicationPackageManager
+android.app.ContextImpl$ApplicationPackageManager$PackageRemovedReceiver
+android.app.ContextImpl$ApplicationPackageManager$ResourceName
+android.app.ContextImpl$SharedPreferencesImpl
android.app.Dialog
android.app.ExpandableListActivity
android.app.IActivityManager
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 0c1e0602..6795bdd 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -176,11 +176,21 @@
public IBackupTransport transport;
public IRestoreObserver observer;
public long token;
+ public PackageInfo pkgInfo;
+
+ RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
+ long _token, PackageInfo _pkg) {
+ transport = _transport;
+ observer = _obs;
+ token = _token;
+ pkgInfo = _pkg;
+ }
RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
transport = _transport;
observer = _obs;
token = _token;
+ pkgInfo = null;
}
}
@@ -210,10 +220,16 @@
File mJournalDir;
File mJournal;
- // Keep a log of all the apps we've ever backed up
+ // Keep a log of all the apps we've ever backed up, and what the
+ // dataset tokens are for both the current backup dataset and
+ // the ancestral dataset.
private File mEverStored;
HashSet<String> mEverStoredApps = new HashSet<String>();
+ File mTokenFile;
+ long mAncestralToken = 0;
+ long mCurrentToken = 0;
+
// Persistently track the need to do a full init
static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
HashSet<String> mPendingInits = new HashSet<String>(); // transport names
@@ -277,7 +293,7 @@
RestoreParams params = (RestoreParams)msg.obj;
Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
(new PerformRestoreTask(params.transport, params.observer,
- params.token)).run();
+ params.token, params.pkgInfo)).run();
break;
}
@@ -475,6 +491,16 @@
private void initPackageTracking() {
if (DEBUG) Log.v(TAG, "Initializing package tracking");
+ // Remember our ancestral dataset
+ mTokenFile = new File(mBaseStateDir, "ancestral");
+ try {
+ RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
+ mAncestralToken = tf.readLong();
+ mCurrentToken = tf.readLong();
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to read token file", e);
+ }
+
// Keep a log of what apps we've ever backed up. Because we might have
// rebooted in the middle of an operation that was removing something from
// this log, we sanity-check its contents here and reconstruct it.
@@ -607,6 +633,9 @@
mEverStoredApps.clear();
mEverStored.delete();
+ mCurrentToken = 0;
+ writeRestoreTokens();
+
// Remove all the state files
for (File sf : stateFileDir.listFiles()) {
// ... but don't touch the needs-init sentinel
@@ -880,7 +909,7 @@
addPackageParticipantsLockedInner(packageName, allApps);
}
- // Called from the backup thread: record that the given app has been successfully
+ // Called from the backup task: record that the given app has been successfully
// backed up at least once
void logBackupComplete(String packageName) {
if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
@@ -938,6 +967,18 @@
}
}
+ // Record the current and ancestral backup tokens persistently
+ void writeRestoreTokens() {
+ try {
+ RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
+ af.writeLong(mAncestralToken);
+ af.writeLong(mCurrentToken);
+ af.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to write token file:", e);
+ }
+ }
+
// Return the given transport
private IBackupTransport getTransport(String transportName) {
synchronized (mTransports) {
@@ -1154,6 +1195,16 @@
Log.e(TAG, "Error in backup thread", e);
status = BackupConstants.TRANSPORT_ERROR;
} finally {
+ // If everything actually went through and this is the first time we've
+ // done a backup, we can now record what the current backup dataset token
+ // is.
+ if ((mCurrentToken == 0) && (status != BackupConstants.TRANSPORT_OK)) {
+ try {
+ mCurrentToken = mTransport.getCurrentRestoreSet();
+ } catch (RemoteException e) { /* cannot happen */ }
+ writeRestoreTokens();
+ }
+
// 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
@@ -1395,6 +1446,7 @@
private IBackupTransport mTransport;
private IRestoreObserver mObserver;
private long mToken;
+ private PackageInfo mTargetPackage;
private File mStateDir;
class RestoreRequest {
@@ -1408,11 +1460,11 @@
}
PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
- long restoreSetToken) {
+ long restoreSetToken, PackageInfo targetPackage) {
mTransport = transport;
- Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver);
mObserver = observer;
mToken = restoreSetToken;
+ mTargetPackage = targetPackage;
try {
mStateDir = new File(mBaseStateDir, transport.transportDirName());
@@ -1424,7 +1476,8 @@
public void run() {
long startRealtime = SystemClock.elapsedRealtime();
if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
- + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken));
+ + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
+ + " mTargetPackage=" + mTargetPackage);
/**
* Restore sequence:
*
@@ -1441,7 +1494,7 @@
*
* On errors, we try our best to recover and move on to the next
* application, but if necessary we abort the whole operation --
- * the user is waiting, after al.
+ * the user is waiting, after all.
*/
int error = -1; // assume error
@@ -1459,7 +1512,12 @@
restorePackages.add(omPackage);
List<PackageInfo> agentPackages = allAgentPackages();
- restorePackages.addAll(agentPackages);
+ if (mTargetPackage == null) {
+ restorePackages.addAll(agentPackages);
+ } else {
+ // Just one package to attempt restore of
+ restorePackages.add(mTargetPackage);
+ }
// let the observer know that we're running
if (mObserver != null) {
@@ -1641,6 +1699,13 @@
}
}
+ // If this was a restoreAll operation, record that this was our
+ // ancestral dataset
+ if (mTargetPackage == null) {
+ mAncestralToken = mToken;
+ writeRestoreTokens();
+ }
+
// done; we can finally release the wakelock
mWakelock.release();
}
@@ -2219,7 +2284,7 @@
}
}
- public synchronized int performRestore(long token, IRestoreObserver observer) {
+ public synchronized int restoreAll(long token, IRestoreObserver observer) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"performRestore");
@@ -2249,6 +2314,55 @@
return -1;
}
+ public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
+ if (DEBUG) Log.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
+
+ PackageInfo app = null;
+ try {
+ app = mPackageManager.getPackageInfo(packageName, 0);
+ } catch (NameNotFoundException nnf) {
+ Log.w(TAG, "Asked to restore nonexistent pkg " + packageName);
+ return -1;
+ }
+
+ // If the caller is not privileged and is not coming from the target
+ // app's uid, throw a permission exception back to the caller.
+ int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
+ Binder.getCallingPid(), Binder.getCallingUid());
+ if ((perm == PackageManager.PERMISSION_DENIED) &&
+ (app.applicationInfo.uid != Binder.getCallingUid())) {
+ Log.w(TAG, "restorePackage: bad packageName=" + packageName
+ + " or calling uid=" + Binder.getCallingUid());
+ throw new SecurityException("No permission to restore other packages");
+ }
+
+ // So far so good; we're allowed to try to restore this package. Now
+ // check whether there is data for it in the current dataset, falling back
+ // to the ancestral dataset if not.
+ long token = mAncestralToken;
+ synchronized (mQueueLock) {
+ if (mEverStoredApps.contains(packageName)) {
+ token = mCurrentToken;
+ }
+ }
+
+ // If we didn't come up with a place to look -- no ancestral dataset and
+ // the app has never been backed up from this device -- there's nothing
+ // to do but return failure.
+ if (token == 0) {
+ return -1;
+ }
+
+ // Ready to go: enqueue the restore request and claim success
+ long oldId = Binder.clearCallingIdentity();
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = new RestoreParams(mRestoreTransport, observer, token, app);
+ mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
+ return 0;
+ }
+
public synchronized void endRestoreSession() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"endRestoreSession");
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index aa4956f..4259016 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -43,6 +43,8 @@
import com.android.internal.telephony.Phone;
+import com.android.server.connectivity.Tethering;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -62,6 +64,9 @@
private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
"android.telephony.apn-restore";
+
+ private Tethering mTethering;
+
/**
* Sometimes we want to refer to the individual network state
* trackers separately, and sometimes we just want to treat them
@@ -308,6 +313,8 @@
continue;
}
}
+
+ mTethering = new Tethering(mContext);
}
@@ -784,6 +791,13 @@
"ConnectivityService");
}
+ // TODO Make this a special check when it goes public
+ private void enforceTetherChangePermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE,
+ "ConnectivityService");
+ }
+
/**
* Handle a {@code DISCONNECTED} event. If this pertains to the non-active
* network, we ignore it. If it is for the active network, we send out a
@@ -1368,4 +1382,28 @@
}
}
}
+
+ // javadoc from interface
+ public boolean tether(String iface) {
+ enforceTetherChangePermission();
+ return mTethering.tether(iface);
+ }
+
+ // javadoc from interface
+ public boolean untether(String iface) {
+ enforceTetherChangePermission();
+ return mTethering.untether(iface);
+ }
+
+ // TODO - move iface listing, queries, etc to new module
+ // javadoc from interface
+ public String[] getTetherableIfaces() {
+ enforceAccessPermission();
+ return mTethering.getTetherableIfaces();
+ }
+
+ public String[] getTetheredIfaces() {
+ enforceAccessPermission();
+ return mTethering.getTetheredIfaces();
+ }
}
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 9d69564..c94450b 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -53,6 +53,19 @@
private final Context mContext;
private final WakeLock mWakeLock; // held while there is a pending route change
+ private boolean mHandleTTY;
+ private int mTTYState;
+ private AudioManager mAudioManager = null;
+
+ // special use of bits in headset state received from kernel made by some
+ // platforms to indicate changes in TTY mode.
+ private static final int BIT_TTY_OFF = 0;
+ private static final int BIT_TTY_FULL = (1 << 2);
+ private static final int BIT_TTY_VCO = (1 << 5);
+ private static final int BIT_TTY_HCO = (1 << 6);
+ private static final int TTY_BITS_MASK = (BIT_TTY_FULL | BIT_TTY_VCO | BIT_TTY_HCO);
+
+
public HeadsetObserver(Context context) {
mContext = context;
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -61,6 +74,11 @@
startObserving(HEADSET_UEVENT_MATCH);
+ // read settings for TTY mode indication method
+ mHandleTTY = context.getResources().getBoolean(
+ com.android.internal.R.bool.tty_mode_uses_headset_events);
+ mTTYState = BIT_TTY_OFF;
+
init(); // set initial status
}
@@ -100,6 +118,39 @@
}
private synchronized final void update(String newName, int newState) {
+ // handle TTY state change first
+ if (mHandleTTY) {
+ int ttyState = newState & TTY_BITS_MASK;
+ if (ttyState != mTTYState) {
+ String ttyMode;
+
+ switch (ttyState) {
+ case BIT_TTY_FULL:
+ ttyMode = "tty_full";
+ break;
+ case BIT_TTY_VCO:
+ ttyMode = "tty_vco";
+ break;
+ case BIT_TTY_HCO:
+ ttyMode = "tty_hco";
+ break;
+ case BIT_TTY_OFF:
+ ttyMode = "tty_off";
+ break;
+ default:
+ ttyMode = "tty_invalid";
+ break;
+
+ }
+ if (ttyMode != "tty_invalid") {
+ mTTYState = ttyState;
+ if (mAudioManager == null) {
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ }
+ mAudioManager.setParameters("tty_mode="+ttyMode);
+ }
+ }
+ }
// Retain only relevant bits
int headsetState = newState & SUPPORTED_HEADSETS;
int newOrOld = headsetState | mHeadsetState;
diff --git a/services/java/com/android/server/NetStatService.java b/services/java/com/android/server/NetStatService.java
index 0834405..ac3445e 100644
--- a/services/java/com/android/server/NetStatService.java
+++ b/services/java/com/android/server/NetStatService.java
@@ -27,11 +27,11 @@
}
public long getMobileTxPackets() {
- return TrafficStats.getMobileTxPkts();
+ return TrafficStats.getMobileTxPackets();
}
public long getMobileRxPackets() {
- return TrafficStats.getMobileRxPkts();
+ return TrafficStats.getMobileRxPackets();
}
public long getMobileTxBytes() {
@@ -43,11 +43,11 @@
}
public long getTotalTxPackets() {
- return TrafficStats.getTotalTxPkts();
+ return TrafficStats.getTotalTxPackets();
}
public long getTotalRxPackets() {
- return TrafficStats.getTotalRxPkts();
+ return TrafficStats.getTotalRxPackets();
}
public long getTotalTxBytes() {
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index b34b50a..d41aacf 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -334,9 +334,9 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
try {
- String cmd = "tether dns set ";
+ String cmd = "tether dns set";
for (String s : dns) {
- cmd += InetAddress.getByName(s).toString() + " ";
+ cmd += " " + InetAddress.getByName(s).getHostAddress();
}
mConnector.doCommand(cmd);
} catch (UnknownHostException e) {
@@ -373,14 +373,16 @@
return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
}
- public void attachPppd(String tty, String localAddr, String remoteAddr)
- throws IllegalStateException {
+ public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
+ String dns2Addr) throws IllegalStateException {
try {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("pppd attach %s %s %s", tty,
- InetAddress.getByName(localAddr).toString(),
- InetAddress.getByName(localAddr).toString()));
+ mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
+ InetAddress.getByName(localAddr).getHostAddress(),
+ InetAddress.getByName(remoteAddr).getHostAddress(),
+ InetAddress.getByName(dns1Addr).getHostAddress(),
+ InetAddress.getByName(dns2Addr).getHostAddress()));
} catch (UnknownHostException e) {
throw new IllegalStateException("Error resolving addr", e);
}
@@ -392,4 +394,3 @@
mConnector.doCommand(String.format("pppd detach %s", tty));
}
}
-
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 22447ed..ee1cc8d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -529,7 +529,8 @@
// The system server has to run all of the time, so it needs to be
// as efficient as possible with its memory usage.
VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
-
+ VMRuntime.getRuntime().startJitCompilation();
+
System.loadLibrary("android_servers");
init1(args);
}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
new file mode 100644
index 0000000..f685383
--- /dev/null
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.INetworkManagementEventObserver;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.ArrayList;
+/**
+ * @hide
+ */
+public class Tethering extends INetworkManagementEventObserver.Stub {
+
+ private Notification mTetheringNotification;
+ private Context mContext;
+ private final String TAG = "Tethering";
+
+ private boolean mPlaySounds = false;
+
+ private ArrayList<String> mAvailableIfaces;
+ private ArrayList<String> mActiveIfaces;
+
+ private ArrayList<String> mActiveTtys;
+
+ private BroadcastReceiver mStateReceiver;
+
+ public Tethering(Context context) {
+ Log.d(TAG, "Tethering starting");
+ mContext = context;
+
+ // register for notifications from NetworkManagement Service
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+ try {
+ service.registerObserver(this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error registering observer :" + e);
+ }
+
+ mAvailableIfaces = new ArrayList<String>();
+ mActiveIfaces = new ArrayList<String>();
+ mActiveTtys = new ArrayList<String>();
+
+ // TODO - remove this hack after real USB connections are detected.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
+ filter.addAction(Intent.ACTION_UMS_CONNECTED);
+ mStateReceiver = new UMSStateReceiver();
+ mContext.registerReceiver(mStateReceiver, filter);
+ }
+
+ public synchronized void interfaceLinkStatusChanged(String iface, boolean link) {
+ Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
+ }
+
+ public synchronized void interfaceAdded(String iface) {
+ if (mActiveIfaces.contains(iface)) {
+ Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring");
+ return;
+ }
+ if (mAvailableIfaces.contains(iface)) {
+ Log.e(TAG, "available iface (" + iface + ") readded, ignoring");
+ return;
+ }
+ mAvailableIfaces.add(iface);
+ Log.d(TAG, "interfaceAdded :" + iface);
+ sendTetherStateChangedBroadcast();
+ }
+
+ public synchronized void interfaceRemoved(String iface) {
+ if (mActiveIfaces.contains(iface)) {
+ Log.d(TAG, "removed an active iface (" + iface + ")");
+ untether(iface);
+ }
+ if (mAvailableIfaces.contains(iface)) {
+ mAvailableIfaces.remove(iface);
+ Log.d(TAG, "interfaceRemoved " + iface);
+ sendTetherStateChangedBroadcast();
+ }
+ }
+
+ public synchronized boolean tether(String iface) {
+ Log.d(TAG, "Tethering " + iface);
+
+ if (!mAvailableIfaces.contains(iface)) {
+ Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
+ return false;
+ }
+ if (mActiveIfaces.contains(iface)) {
+ Log.e(TAG, "Tried to Tether an already Tethered iface :" + iface + ", ignoring");
+ return false;
+ }
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ if (mActiveIfaces.size() == 0) {
+ try {
+ service.setIpForwardingEnabled(true);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in setIpForwardingEnabled(true) :" + e);
+ return false;
+ }
+
+ try {
+ // TODO - don't hardcode this - though with non-conf values (un-routable)
+ // maybe it's not a big deal
+ service.startTethering("169.254.2.1", "169.254.2.64");
+ } catch (Exception e) {
+ Log.e(TAG, "Error in startTethering :" + e);
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception ee) {}
+ return false;
+ }
+
+ try {
+ // TODO - maybe use the current connection's dns servers for this
+ String[] dns = new String[2];
+ dns[0] = new String("8.8.8.8");
+ dns[1] = new String("4.2.2.2");
+ service.setDnsForwarders(dns);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in setDnsForwarders :" + e);
+ try {
+ service.stopTethering();
+ } catch (Exception ee) {}
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception ee) {}
+ }
+ }
+
+ try {
+ service.tetherInterface(iface);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in tetherInterface :" + e);
+ if (mActiveIfaces.size() == 0) {
+ try {
+ service.stopTethering();
+ } catch (Exception ee) {}
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception ee) {}
+ }
+ return false;
+ }
+
+ try {
+ // TODO - use the currently active external iface
+ service.enableNat (iface, "rmnet0");
+ } catch (Exception e) {
+ Log.e(TAG, "Error in enableNat :" + e);
+ try {
+ service.untetherInterface(iface);
+ } catch (Exception ee) {}
+ if (mActiveIfaces.size() == 0) {
+ try {
+ service.stopTethering();
+ } catch (Exception ee) {}
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception ee) {}
+ }
+ return false;
+ }
+ mAvailableIfaces.remove(iface);
+ mActiveIfaces.add(iface);
+ Log.d(TAG, "Tethered " + iface);
+ sendTetherStateChangedBroadcast();
+ return true;
+ }
+
+ public synchronized boolean untether(String iface) {
+ Log.d(TAG, "Untethering " + iface);
+
+ if (mAvailableIfaces.contains(iface)) {
+ Log.e(TAG, "Tried to Untether an available iface :" + iface);
+ return false;
+ }
+ if (!mActiveIfaces.contains(iface)) {
+ Log.e(TAG, "Tried to Untether an inactive iface :" + iface);
+ return false;
+ }
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ // none of these errors are recoverable - ie, multiple calls won't help
+ // and the user can't do anything. Basically a reboot is required and probably
+ // the device is misconfigured or something bad has happend.
+ // Because of this, we should try to unroll as much state as we can.
+ try {
+ service.disableNat(iface, "rmnet0");
+ } catch (Exception e) {
+ Log.e(TAG, "Error in disableNat :" + e);
+ }
+ try {
+ service.untetherInterface(iface);
+ } catch (Exception e) {
+ Log.e(TAG, "Error untethering " + iface + ", :" + e);
+ }
+ mActiveIfaces.remove(iface);
+ mAvailableIfaces.add(iface);
+
+ if (mActiveIfaces.size() == 0) {
+ Log.d(TAG, "no active tethers - turning down dhcp/ipforward");
+ try {
+ service.stopTethering();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in stopTethering :" + e);
+ }
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in setIpForwardingEnabled(false) :" + e);
+ }
+ }
+ sendTetherStateChangedBroadcast();
+ Log.d(TAG, "Untethered " + iface);
+ return true;
+ }
+
+ private void sendTetherStateChangedBroadcast() {
+ Intent broadcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+ broadcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ broadcast.putExtra(ConnectivityManager.EXTRA_AVAILABLE_TETHER_COUNT,
+ mAvailableIfaces.size());
+ broadcast.putExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER_COUNT, mActiveIfaces.size());
+ mContext.sendBroadcast(broadcast);
+
+ // for USB we only have the one, so don't have to deal with additional
+ if (mAvailableIfaces.size() > 0) {
+ // Check if the user wants to be bothered
+ boolean tellUser = (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.TETHER_NOTIFY, 0) == 1);
+
+ if (tellUser) {
+ showTetherAvailableNotification();
+ }
+ } else if (mActiveIfaces.size() > 0) {
+ showTetheredNotification();
+ } else {
+ clearNotification();
+ }
+ }
+
+ private void showTetherAvailableNotification() {
+ NotificationManager notificationManager = (NotificationManager)mContext.
+ getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
+
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ Resources r = Resources.getSystem();
+ CharSequence title = r.getText(com.android.internal.R.string.
+ tether_available_notification_title);
+ CharSequence message = r.getText(com.android.internal.R.string.
+ tether_available_notification_message);
+
+ if(mTetheringNotification == null) {
+ mTetheringNotification = new Notification();
+ mTetheringNotification.when = 0;
+ }
+ mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
+
+ boolean playSounds = false;
+ //playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
+ if (playSounds) {
+ mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
+ } else {
+ mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
+ }
+
+ mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mTetheringNotification.tickerText = title;
+ mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
+
+ notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
+
+ }
+
+ private void showTetheredNotification() {
+ NotificationManager notificationManager = (NotificationManager)mContext.
+ getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
+
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ Resources r = Resources.getSystem();
+ CharSequence title = r.getText(com.android.internal.R.string.
+ tether_stop_notification_title);
+ CharSequence message = r.getText(com.android.internal.R.string.
+ tether_stop_notification_message);
+
+ if(mTetheringNotification == null) {
+ mTetheringNotification = new Notification();
+ mTetheringNotification.when = 0;
+ }
+ mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
+
+ boolean playSounds = false;
+ //playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
+ if (playSounds) {
+ mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
+ } else {
+ mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
+ }
+
+ mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mTetheringNotification.tickerText = title;
+ mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
+
+ notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
+ }
+
+ private void clearNotification() {
+ NotificationManager notificationManager = (NotificationManager)mContext.
+ getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager != null && mTetheringNotification != null) {
+ notificationManager.cancel(mTetheringNotification.icon);
+ mTetheringNotification = null;
+ }
+ }
+
+
+
+
+// TODO - remove this hack after we get proper USB detection
+ private class UMSStateReceiver extends BroadcastReceiver {
+ public void onReceive(Context content, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_UMS_CONNECTED)) {
+ Tethering.this.handleTtyConnect();
+ } else if (intent.getAction().equals(Intent.ACTION_UMS_DISCONNECTED)) {
+ Tethering.this.handleTtyDisconnect();
+ }
+ }
+ }
+
+ private synchronized void handleTtyConnect() {
+ Log.d(TAG, "handleTtyConnect");
+ // for each of the available Tty not already supported by a ppp session,
+ // create a ppp session
+ // TODO - this should be data-driven rather than hard coded.
+ String[] allowedTtys = new String[1];
+ allowedTtys[0] = new String("ttyGS0");
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ String[] availableTtys;
+ try {
+ availableTtys = service.listTtys();
+ } catch (RemoteException e) {
+ Log.e(TAG, "error listing Ttys :" + e);
+ return;
+ }
+
+ for (String tty : availableTtys) {
+ for (String pattern : allowedTtys) {
+ if (tty.matches(pattern)) {
+ synchronized (this) {
+ if (!mActiveTtys.contains(tty)) {
+ // TODO - don't hardcode this
+ try {
+ // local, remote, dns
+ service.attachPppd(tty, "169.254.1.128", "169.254.1.1",
+ "169.254.1.128", "0.0.0.0");
+ } catch (Exception e) {
+ Log.e(TAG, "error calling attachPppd: " + e);
+ return;
+ }
+ Log.d(TAG, "started Pppd on tty " + tty);
+ mActiveTtys.add(tty);
+ // TODO - remove this after we detect the new iface
+ interfaceAdded("ppp0");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private synchronized void handleTtyDisconnect() {
+ Log.d(TAG, "handleTtyDisconnect");
+
+ // TODO - this should be data-driven rather than hard coded.
+ String[] allowedTtys = new String[1];
+ allowedTtys[0] = new String("ttyGS0");
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ String[] availableTtys;
+ try {
+ availableTtys = service.listTtys();
+ } catch (RemoteException e) {
+ Log.e(TAG, "error listing Ttys :" + e);
+ return;
+ }
+
+ for (String tty : availableTtys) {
+ for (String pattern : allowedTtys) {
+ if (tty.matches(pattern)) {
+ synchronized (this) {
+ if (mActiveTtys.contains(tty)) {
+ try {
+ service.detachPppd(tty);
+ } catch (Exception e) {
+ Log.e(TAG, "error calling detachPppd on " + tty + " :" + e);
+ }
+ mActiveTtys.remove(tty);
+ // TODO - remove this after we detect the new iface
+ interfaceRemoved("ppp0");
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public synchronized String[] getTetheredIfaces() {
+ int size = mActiveIfaces.size();
+ String[] result = new String[size];
+ size -= 1;
+ for (int i=0; i< size; i++) {
+ result[i] = mActiveIfaces.get(i);
+ }
+ return result;
+ }
+
+ public synchronized String[] getTetherableIfaces() {
+ int size = mAvailableIfaces.size();
+ String[] result = new String[size];
+ size -= 1;
+ for (int i=0; i< size; i++) {
+ result[i] = mActiveIfaces.get(i);
+ }
+ return result;
+ }
+}
diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java
index 0a3411a1..784b781 100644
--- a/services/java/com/android/server/status/NotificationData.java
+++ b/services/java/com/android/server/status/NotificationData.java
@@ -19,7 +19,7 @@
public PendingIntent deleteIntent;
public String toString() {
- return "NotificationData(package=" + pkg + " tickerText=" + tickerText
+ return "NotificationData(package=" + pkg + " id=" + id + " tickerText=" + tickerText
+ " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
+ " deleteIntent=" + deleteIntent
+ " clearable=" + clearable
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
index 6229292..8f1633f 100644
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ b/services/java/com/android/server/status/NotificationViewList.java
@@ -104,10 +104,25 @@
return null;
}
- // gets the index of the notification in its expanded parent view
+ // gets the index of the notification's view in its expanded parent view
int getExpandedIndex(StatusBarNotification notification) {
ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
- return list.size() - indexForKey(list, notification.key) - 1;
+ final IBinder key = notification.key;
+ int index = 0;
+ // (the view order is backwards from this list order)
+ for (int i=list.size()-1; i>=0; i--) {
+ StatusBarNotification item = list.get(i);
+ if (item.key == key) {
+ return index;
+ }
+ if (item.view != null) {
+ index++;
+ }
+ }
+ Log.e(StatusBarService.TAG, "Couldn't find notification in NotificationViewList.");
+ Log.e(StatusBarService.TAG, "notification=" + notification);
+ dump(notification);
+ return 0;
}
void clearViews() {
@@ -156,6 +171,13 @@
list.add(index, notification);
if (StatusBarService.SPEW) {
+ Log.d(StatusBarService.TAG, "NotificationViewList index=" + index);
+ dump(notification);
+ }
+ }
+
+ void dump(StatusBarNotification notification) {
+ if (StatusBarService.SPEW) {
String s = "";
for (int i=0; i<mOngoing.size(); i++) {
StatusBarNotification that = mOngoing.get(i);
@@ -168,7 +190,7 @@
}
s += " ";
}
- Log.d(StatusBarService.TAG, "NotificationViewList ongoing index=" + index + ": " + s);
+ Log.d(StatusBarService.TAG, "NotificationViewList ongoing: " + s);
s = "";
for (int i=0; i<mLatest.size(); i++) {
@@ -182,7 +204,7 @@
}
s += " ";
}
- Log.d(StatusBarService.TAG, "NotificationViewList latest index=" + index + ": " + s);
+ Log.d(StatusBarService.TAG, "NotificationViewList latest: " + s);
}
}
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 7b722a5..55041fb 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -687,7 +687,8 @@
&& (oldData == null
|| oldData.tickerText == null
|| !CharSequences.equals(oldData.tickerText, n.tickerText))) {
- if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+ if (0 == (mDisabled &
+ (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
}
}
@@ -886,7 +887,8 @@
&& n.contentView.getPackage() != null
&& oldData.contentView.getPackage() != null
&& oldData.contentView.getPackage().equals(n.contentView.getPackage())
- && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()) {
+ && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()
+ && notification.view != null) {
mNotificationData.update(notification);
try {
n.contentView.reapply(mContext, notification.contentView);
@@ -1697,6 +1699,10 @@
setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
}
}
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ mTicker.halt();
+ }
}
}
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index c2548b9..70d0e78 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -26,6 +26,8 @@
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
diff --git a/tests/AndroidTests/res/raw/install b/tests/AndroidTests/res/raw/install
new file mode 100644
index 0000000..2ee1f3c
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install
Binary files differ
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
new file mode 100755
index 0000000..163ddd5
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.unit_tests;
+
+import android.net.Uri;
+import android.os.FileUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageStats;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StatFs;
+
+public class PackageManagerTests extends AndroidTestCase {
+ private static final boolean localLOGV = false;
+ public static final String TAG="PackageManagerTests";
+ public final long MAX_WAIT_TIME=60*1000;
+ public final long WAIT_TIME_INCR=10*1000;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ private class PackageInstallObserver extends IPackageInstallObserver.Stub {
+ public int returnCode;
+ private boolean doneFlag = false;
+
+ public void packageInstalled(String packageName, int returnCode) {
+ synchronized(this) {
+ this.returnCode = returnCode;
+ doneFlag = true;
+ notifyAll();
+ }
+ }
+
+ public boolean isDone() {
+ return doneFlag;
+ }
+ }
+
+ abstract class GenericReceiver extends BroadcastReceiver {
+ private boolean doneFlag = false;
+ boolean received = false;
+ Intent intent;
+ IntentFilter filter;
+ abstract boolean notifyNow(Intent intent);
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (notifyNow(intent)) {
+ synchronized (this) {
+ received = true;
+ doneFlag = true;
+ this.intent = intent;
+ notifyAll();
+ }
+ }
+ }
+
+ public boolean isDone() {
+ return doneFlag;
+ }
+
+ public void setFilter(IntentFilter filter) {
+ this.filter = filter;
+ }
+ }
+
+ class InstallReceiver extends GenericReceiver {
+ String pkgName;
+
+ InstallReceiver(String pkgName) {
+ this.pkgName = pkgName;
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package");
+ super.setFilter(filter);
+ }
+
+ public boolean notifyNow(Intent intent) {
+ String action = intent.getAction();
+ if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ return false;
+ }
+ Uri data = intent.getData();
+ String installedPkg = data.getEncodedSchemeSpecificPart();
+ if (pkgName.equals(installedPkg)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ PackageManager getPm() {
+ return mContext.getPackageManager();
+ }
+
+ public boolean invokeInstallPackage(Uri packageURI, int flags,
+ final String pkgName, GenericReceiver receiver) throws Exception {
+ PackageInstallObserver observer = new PackageInstallObserver();
+ final boolean received = false;
+ mContext.registerReceiver(receiver, receiver.filter);
+ try {
+ // Wait on observer
+ synchronized(observer) {
+ getPm().installPackage(packageURI, observer, flags, null);
+ long waitTime = 0;
+ while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+ observer.wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ }
+ if(!observer.isDone()) {
+ throw new Exception("Timed out waiting for packageInstalled callback");
+ }
+ if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ return false;
+ }
+ synchronized (receiver) {
+ // Verify we received the broadcast
+ waitTime = 0;
+ while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+ receiver.wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ }
+ if(!receiver.isDone()) {
+ throw new Exception("Timed out waiting for PACKAGE_ADDED notification");
+ }
+ return receiver.received;
+ }
+ }
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ Uri getInstallablePackage(int fileResId, File outFile) {
+ Resources res = mContext.getResources();
+ InputStream is = null;
+ try {
+ is = res.openRawResource(fileResId);
+ } catch (NotFoundException e) {
+ failStr("Failed to load resource with id: " + fileResId);
+ }
+ FileUtils.setPermissions(outFile.getPath(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
+ -1, -1);
+ assertTrue(FileUtils.copyToFile(is, outFile));
+ FileUtils.setPermissions(outFile.getPath(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
+ -1, -1);
+ return Uri.fromFile(outFile);
+ }
+
+ private PackageParser.Package parsePackage(Uri packageURI) {
+ final String archiveFilePath = packageURI.getPath();
+ PackageParser packageParser = new PackageParser(archiveFilePath);
+ File sourceFile = new File(archiveFilePath);
+ DisplayMetrics metrics = new DisplayMetrics();
+ metrics.setToDefaults();
+ return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+ }
+
+ private void assertInstall(String pkgName, int flags) {
+ try {
+ ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
+ assertNotNull(info);
+ assertEquals(pkgName, info.packageName);
+ File dataDir = Environment.getDataDirectory();
+ String appInstallPath = new File(dataDir, "app").getPath();
+ String drmInstallPath = new File(dataDir, "app-private").getPath();
+ File srcDir = new File(info.sourceDir);
+ String srcPath = srcDir.getParent();
+ File publicSrcDir = new File(info.publicSourceDir);
+ String publicSrcPath = publicSrcDir.getParent();
+
+ if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
+ assertTrue((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+ assertEquals(srcPath, drmInstallPath);
+ assertEquals(publicSrcPath, appInstallPath);
+ } else {
+ assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+ if ((flags & PackageManager.INSTALL_ON_SDCARD) != 0) {
+ assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+ // Hardcoded for now
+ assertTrue(srcPath.startsWith("/asec"));
+ assertTrue(publicSrcPath.startsWith("/asec"));
+ } else {
+ assertEquals(srcPath, appInstallPath);
+ assertEquals(publicSrcPath, appInstallPath);
+ assertFalse((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+ }
+ }
+ } catch (NameNotFoundException e) {
+ failStr("failed with exception : " + e);
+ }
+ }
+
+ class InstallParams {
+ String outFileName;
+ Uri packageURI;
+ PackageParser.Package pkg;
+ InstallParams(PackageParser.Package pkg, String outFileName, Uri packageURI) {
+ this.outFileName = outFileName;
+ this.packageURI = packageURI;
+ this.pkg = pkg;
+ }
+ }
+
+ /*
+ * Utility function that reads a apk bundled as a raw resource
+ * copies it into own data directory and invokes
+ * PackageManager api to install it.
+ */
+ public InstallParams installFromRawResource(int flags, boolean cleanUp) {
+ String outFileName = "install.apk";
+ File filesDir = mContext.getFilesDir();
+ File outFile = new File(filesDir, outFileName);
+ Uri packageURI = getInstallablePackage(R.raw.install, outFile);
+ PackageParser.Package pkg = parsePackage(packageURI);
+ assertNotNull(pkg);
+ InstallReceiver receiver = new InstallReceiver(pkg.packageName);
+ try {
+ try {
+ assertTrue(invokeInstallPackage(packageURI, flags,
+ pkg.packageName, receiver));
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
+ }
+ // Verify installed information
+ assertInstall(pkg.packageName, flags);
+ return new InstallParams(pkg, outFileName, packageURI);
+ } finally {
+ if (cleanUp) {
+ getPm().deletePackage(pkg.packageName, null, 0);
+ if (outFile != null && outFile.exists()) {
+ outFile.delete();
+ }
+ }
+ }
+ }
+
+ @MediumTest
+ public void testInstallNormalInternal() {
+ installFromRawResource(0, true);
+ }
+
+ @MediumTest
+ public void testInstallFwdLockedInternal() {
+ installFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, true);
+ }
+
+ @MediumTest
+ public void testInstallSdcard() {
+ installFromRawResource(PackageManager.INSTALL_ON_SDCARD, true);
+ }
+
+ /* ------------------------- Test replacing packages --------------*/
+ class ReplaceReceiver extends GenericReceiver {
+ String pkgName;
+ boolean removed = false;
+
+ ReplaceReceiver(String pkgName) {
+ this.pkgName = pkgName;
+ filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ super.setFilter(filter);
+ }
+
+ public boolean notifyNow(Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+ Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+ Uri data = intent.getData();
+ String installedPkg = data.getEncodedSchemeSpecificPart();
+ if (pkgName.equals(installedPkg)) {
+ if (removed) {
+ return true;
+ } else {
+ removed = true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ /*
+ * Utility function that reads a apk bundled as a raw resource
+ * copies it into own data directory and invokes
+ * PackageManager api to install first and then replace it
+ * again.
+ */
+ public void replaceFromRawResource(int flags) {
+ InstallParams ip = installFromRawResource(flags, false);
+ boolean result = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
+ GenericReceiver receiver;
+ if (result) {
+ receiver = new ReplaceReceiver(ip.pkg.packageName);
+ } else {
+ receiver = new InstallReceiver(ip.pkg.packageName);
+ }
+ try {
+ try {
+ assertEquals(invokeInstallPackage(ip.packageURI, flags,
+ ip.pkg.packageName, receiver), result);
+ if (result) {
+ assertInstall(ip.pkg.packageName, flags);
+ }
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
+ }
+ } finally {
+ getPm().deletePackage(ip.pkg.packageName, null, 0);
+ File outFile = new File(ip.outFileName);
+ if (outFile != null && outFile.exists()) {
+ outFile.delete();
+ }
+ }
+ }
+
+ @MediumTest
+ public void testReplaceFailNormalInternal() {
+ replaceFromRawResource(0);
+ }
+
+ @MediumTest
+ public void testReplaceFailFwdLockedInternal() {
+ replaceFromRawResource(PackageManager.INSTALL_FORWARD_LOCK);
+ }
+
+ @MediumTest
+ public void testReplaceFailSdcard() {
+ replaceFromRawResource(PackageManager.INSTALL_ON_SDCARD);
+ }
+
+ void failStr(String errMsg) {
+ Log.w(TAG, "errMsg="+errMsg);
+ fail(errMsg);
+ }
+ void failStr(Exception e) {
+ Log.w(TAG, "e.getMessage="+e.getMessage());
+ Log.w(TAG, "e="+e);
+ }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index e8a66c1..2667520 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -670,7 +670,12 @@
public boolean onCreateWindow(WebView view, boolean dialog,
boolean userGesture, Message resultMsg) {
if (!mCanOpenWindows) {
- return false;
+ // We can't open windows, so just send null back.
+ WebView.WebViewTransport transport =
+ (WebView.WebViewTransport) resultMsg.obj;
+ transport.setWebView(null);
+ resultMsg.sendToTarget();
+ return true;
}
// We never display the new window, just create the view and
@@ -688,6 +693,11 @@
resultMsg.sendToTarget();
return true;
}
+
+ @Override
+ public void onCloseWindow(WebView view) {
+ view.destroy();
+ }
};
private static class NewWindowWebView extends WebView {
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index ffc2cbc..f548145 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -121,6 +121,32 @@
}
},
+ new Test("Bad resource #2") {
+ public void run()
+ {
+ Notification n = new Notification(NotificationTestList.this,
+ R.drawable.ic_statusbar_missedcall,
+ null, System.currentTimeMillis()-(1000*60*60*24),
+ "(453) 123-2328",
+ "", null);
+ n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
+ mNM.notify(2, n);
+ }
+ },
+
+ new Test("Bad resource #3") {
+ public void run()
+ {
+ Notification n = new Notification(NotificationTestList.this,
+ R.drawable.ic_statusbar_missedcall,
+ null, System.currentTimeMillis()-(1000*60*60*24),
+ "(453) 123-2328",
+ "", null);
+ n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
+ mNM.notify(3, n);
+ }
+ },
+
new Test("Times") {
public void run()
{
@@ -405,6 +431,16 @@
}
},
+ new Test("Persistent #3") {
+ public void run() {
+ Notification n = new Notification(R.drawable.icon2, "tock tock tock",
+ System.currentTimeMillis());
+ n.setLatestEventInfo(NotificationTestList.this, "Persistent #3",
+ "Notify me!!!", makeIntent());
+ mNM.notify(3, n);
+ }
+ },
+
new Test("Persistent #2 Vibrate") {
public void run() {
Notification n = new Notification(R.drawable.icon2, "tock tock tock",
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 275e5cb6..06506fb 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -62,6 +62,11 @@
mStatusBarManager.disable(StatusBarManager.DISABLE_NOTIFICATION_ALERTS);
}
},
+ new Test("Disable Ticker") {
+ public void run() {
+ mStatusBarManager.disable(StatusBarManager.DISABLE_NOTIFICATION_TICKER);
+ }
+ },
new Test("Disable Expand in 3 sec.") {
public void run() {
mHandler.postDelayed(new Runnable() {
@@ -80,7 +85,7 @@
}, 3000);
}
},
- new Test("Disable Both in 3 sec.") {
+ new Test("Disable Expand + Notifications in 3 sec.") {
public void run() {
mHandler.postDelayed(new Runnable() {
public void run() {
@@ -90,7 +95,12 @@
}, 3000);
}
},
- new Test("Disable None in 3 sec.") {
+ new Test("Enable everything") {
+ public void run() {
+ mStatusBarManager.disable(0);
+ }
+ },
+ new Test("Enable everything in 3 sec.") {
public void run() {
mHandler.postDelayed(new Runnable() {
public void run() {