diff options
30 files changed, 1485 insertions, 254 deletions
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 00bd54e..2690182 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -935,6 +935,7 @@ void CameraService::Client::handleShutter( mHardware->getRawHeap()); mSurface->registerBuffers(buffers); + IPCThreadState::self()->flushCommands(); } } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 7625c04..cd22fa1 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -588,7 +588,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ private void updateVoiceButton(boolean empty) { int visibility = View.GONE; - if (mSearchable.getVoiceSearchEnabled() && empty) { + if ((mAppSearchData == null || !mAppSearchData.getBoolean( + SearchManager.DISABLE_VOICE_SEARCH, false)) + && mSearchable.getVoiceSearchEnabled() && empty) { Intent testIntent = null; if (mSearchable.getVoiceSearchLaunchWebSearch()) { testIntent = mVoiceWebSearchIntent; diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index a1ca707..2e9cd96 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -395,6 +395,14 @@ public class SearchManager public final static String CONTEXT_IS_VOICE = "android.search.CONTEXT_IS_VOICE"; /** + * This means that the voice icon should not be shown at all, because the + * current search engine does not support voice search. + * @hide + */ + public final static String DISABLE_VOICE_SEARCH + = "android.search.DISABLE_VOICE_SEARCH"; + + /** * Reference to the shared system search service. */ private static ISearchManager mService; diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java index a7d036f..b016088 100644 --- a/core/java/android/content/SyncOperation.java +++ b/core/java/android/content/SyncOperation.java @@ -90,7 +90,7 @@ public class SyncOperation implements Comparable { private String toKey() { StringBuilder sb = new StringBuilder(); sb.append("authority: ").append(authority); - sb.append(" account: ").append(account); + sb.append(" account {name=" + account.name + ", type=" + account.type + "}"); sb.append(" extras: "); extrasToStringBuilder(extras, sb, true /* asKey */); return sb.toString(); diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index a8c6f9b..31acb5b 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -35,6 +35,11 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import javax.net.SocketFactory; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; @@ -48,17 +53,33 @@ import org.apache.harmony.xnet.provider.jsse.SSLParameters; /** * SSLSocketFactory implementation with several extra features: + * * <ul> * <li>Timeout specification for SSL handshake operations + * <li>Hostname verification in most cases (see WARNINGs below) * <li>Optional SSL session caching with {@link SSLSessionCache} * <li>Optionally bypass all SSL certificate checks * </ul> - * Note that the handshake timeout does not apply to actual connection. - * If you want a connection timeout as well, use {@link #createSocket()} and - * {@link Socket#connect(SocketAddress, int)}. - * <p> - * On development devices, "setprop socket.relaxsslcheck yes" bypasses all - * SSL certificate checks, for testing with development servers. + * + * The handshake timeout does not apply to actual TCP socket connection. + * If you want a connection timeout as well, use {@link #createSocket()} + * and {@link Socket#connect(SocketAddress, int)}, after which you + * must verify the identity of the server you are connected to. + * + * <p class="caution"><b>Most {@link SSLSocketFactory} implementations do not + * verify the server's identity, allowing man-in-the-middle attacks.</b> + * This implementation does check the server's certificate hostname, but only + * for createSocket variants that specify a hostname. When using methods that + * use {@link InetAddress} or which return an unconnected socket, you MUST + * verify the server's identity yourself to ensure a secure connection.</p> + * + * <p>One way to verify the server's identity is to use + * {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a + * {@link HostnameVerifier} to verify the certificate hostname. + * + * <p>On development devices, "setprop socket.relaxsslcheck yes" bypasses all + * SSL certificate and hostname checks for testing purposes. This setting + * requires root access. */ public class SSLCertificateSocketFactory extends SSLSocketFactory { private static final String TAG = "SSLCertificateSocketFactory"; @@ -71,6 +92,9 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } }; + private static final HostnameVerifier HOSTNAME_VERIFIER = + HttpsURLConnection.getDefaultHostnameVerifier(); + private SSLSocketFactory mInsecureFactory = null; private SSLSocketFactory mSecureFactory = null; @@ -95,7 +119,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 * for none. The socket timeout is reset to 0 after the handshake. - * @return a new SocketFactory with the specified parameters + * @return a new SSLSocketFactory with the specified parameters */ public static SocketFactory getDefault(int handshakeTimeoutMillis) { return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true); @@ -108,7 +132,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 * for none. The socket timeout is reset to 0 after the handshake. * @param cache The {@link SSLClientSessionCache} to use, or null for no cache. - * @return a new SocketFactory with the specified parameters + * @return a new SSLSocketFactory with the specified parameters */ public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) { return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true); @@ -117,13 +141,14 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { /** * Returns a new instance of a socket factory with all SSL security checks * disabled, using an optional handshake timeout and SSL session cache. - * Sockets created using this factory are vulnerable to man-in-the-middle - * attacks! + * + * <p class="caution"><b>Warning:</b> Sockets created using this factory + * are vulnerable to man-in-the-middle attacks!</p> * * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 * for none. The socket timeout is reset to 0 after the handshake. * @param cache The {@link SSLClientSessionCache} to use, or null for no cache. - * @return an insecure SocketFactory with the specified parameters + * @return an insecure SSLSocketFactory with the specified parameters */ public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) { return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false); @@ -145,6 +170,44 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true)); } + /** + * Verify the hostname of the certificate used by the other end of a + * connected socket. You MUST call this if you did not supply a hostname + * to {@link #createSocket()}. It is harmless to call this method + * redundantly if the hostname has already been verified. + * + * <p>Wildcard certificates are allowed to verify any matching hostname, + * so "foo.bar.example.com" is verified if the peer has a certificate + * for "*.example.com". + * + * @param socket An SSL socket which has been connected to a server + * @param hostname The expected hostname of the remote server + * @throws IOException if something goes wrong handshaking with the server + * @throws SSLPeerUnverifiedException if the server cannot prove its identity + * + * @hide + */ + public static void verifyHostname(Socket socket, String hostname) throws IOException { + if (!(socket instanceof SSLSocket)) { + throw new IllegalArgumentException("Attempt to verify non-SSL socket"); + } + + if (!isSslCheckRelaxed()) { + // The code at the start of OpenSSLSocketImpl.startHandshake() + // ensures that the call is idempotent, so we can safely call it. + SSLSocket ssl = (SSLSocket) socket; + ssl.startHandshake(); + + SSLSession session = ssl.getSession(); + if (session == null) { + throw new SSLException("Cannot verify SSL socket without session"); + } + if (!HOSTNAME_VERIFIER.verify(hostname, session)) { + throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname); + } + } + } + private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) { try { SSLContextImpl sslContext = new SSLContextImpl(); @@ -156,10 +219,14 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } } + private static boolean isSslCheckRelaxed() { + return "1".equals(SystemProperties.get("ro.debuggable")) && + "yes".equals(SystemProperties.get("socket.relaxsslcheck")); + } + private synchronized SSLSocketFactory getDelegate() { // Relax the SSL check if instructed (for this factory, or systemwide) - if (!mSecure || ("1".equals(SystemProperties.get("ro.debuggable")) && - "yes".equals(SystemProperties.get("socket.relaxsslcheck")))) { + if (!mSecure || isSslCheckRelaxed()) { if (mInsecureFactory == null) { if (mSecure) { Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***"); @@ -177,13 +244,30 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } } + /** + * {@inheritDoc} + * + * <p>This method verifies the peer's certificate hostname after connecting + * (unless created with {@link #getInsecure(int, SSLSessionCache)}). + */ @Override public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close); s.setHandshakeTimeout(mHandshakeTimeoutMillis); + if (mSecure) { + verifyHostname(s, host); + } return s; } + /** + * Creates a new socket which is not connected to any remote host. + * You must use {@link Socket#connect} to connect the socket. + * + * <p class="caution"><b>Warning:</b> Hostname verification is not performed + * with this method. You MUST verify the server's identity after connecting + * the socket to avoid man-in-the-middle attacks.</p> + */ @Override public Socket createSocket() throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(); @@ -191,6 +275,13 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { return s; } + /** + * {@inheritDoc} + * + * <p class="caution"><b>Warning:</b> Hostname verification is not performed + * with this method. You MUST verify the server's identity after connecting + * the socket to avoid man-in-the-middle attacks.</p> + */ @Override public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort) throws IOException { @@ -200,6 +291,13 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { return s; } + /** + * {@inheritDoc} + * + * <p class="caution"><b>Warning:</b> Hostname verification is not performed + * with this method. You MUST verify the server's identity after connecting + * the socket to avoid man-in-the-middle attacks.</p> + */ @Override public Socket createSocket(InetAddress addr, int port) throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port); @@ -207,19 +305,37 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { return s; } + /** + * {@inheritDoc} + * + * <p>This method verifies the peer's certificate hostname after connecting + * (unless created with {@link #getInsecure(int, SSLSessionCache)}). + */ @Override public Socket createSocket(String host, int port, InetAddress localAddr, int localPort) throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket( host, port, localAddr, localPort); s.setHandshakeTimeout(mHandshakeTimeoutMillis); + if (mSecure) { + verifyHostname(s, host); + } return s; } + /** + * {@inheritDoc} + * + * <p>This method verifies the peer's certificate hostname after connecting + * (unless created with {@link #getInsecure(int, SSLSessionCache)}). + */ @Override public Socket createSocket(String host, int port) throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port); s.setHandshakeTimeout(mHandshakeTimeoutMillis); + if (mSecure) { + verifyHostname(s, host); + } return s; } diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index dedc347..01cc408 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -26,6 +26,7 @@ interface IPowerManager void releaseWakeLock(IBinder lock, int flags); void userActivity(long when, boolean noChangeLights); void userActivityWithForce(long when, boolean noChangeLights, boolean force); + void clearUserActivityTimeout(long now, long timeout); void setPokeLock(int pokey, IBinder lock, String tag); int getSupportedWakeLockFlags(); void setStayOnSetting(int val); diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index a77717f..ce73ae1 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -36,6 +36,8 @@ android:description="@string/permdesc_testDenied" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.BLUETOOTH" /> + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> @@ -1203,8 +1205,12 @@ </application> - <instrumentation - android:name="android.test.InstrumentationTestRunner" - android:targetPackage="com.android.frameworks.coretests" - android:label="Frameworks Core Tests" /> + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.frameworks.coretests" + android:label="Frameworks Core Tests" /> + + <instrumentation android:name="android.bluetooth.BluetoothTestRunner" + android:targetPackage="com.android.frameworks.coretests" + android:label="Bluetooth Tests" /> + </manifest> diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java new file mode 100644 index 0000000..33e9dd7 --- /dev/null +++ b/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java @@ -0,0 +1,94 @@ +/* + * 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.bluetooth; + +import android.content.Context; +import android.test.InstrumentationTestCase; + +/** + * Instrumentation test case for stress test involving rebooting the device. + * <p> + * This test case tests that bluetooth is enabled after a device reboot. Because + * the device will reboot, the instrumentation must be driven by a script on the + * host side. + */ +public class BluetoothRebootStressTest extends InstrumentationTestCase { + private static final String TAG = "BluetoothRebootStressTest"; + private static final String OUTPUT_FILE = "BluetoothRebootStressTestOutput.txt"; + + private BluetoothTestUtils mTestUtils; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + Context context = getInstrumentation().getTargetContext(); + mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + + mTestUtils.close(); + } + + /** + * Test method used to start the test by turning bluetooth on. + */ + public void testStart() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + mTestUtils.enable(adapter); + } + + /** + * Test method used in the middle iterations of the test to check if + * bluetooth is on. Does not toggle bluetooth after the check. Assumes that + * bluetooth has been turned on by {@code #testStart()} + */ + public void testMiddleNoToggle() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + assertTrue(adapter.isEnabled()); + } + + /** + * Test method used in the middle iterations of the test to check if + * bluetooth is on. Toggles bluetooth after the check. Assumes that + * bluetooth has been turned on by {@code #testStart()} + */ + public void testMiddleToggle() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + assertTrue(adapter.isEnabled()); + + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + } + + /** + * Test method used in the stop the test by turning bluetooth off. Assumes + * that bluetooth has been turned on by {@code #testStart()} + */ + public void testStop() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + assertTrue(adapter.isEnabled()); + + mTestUtils.disable(adapter); + } +} diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java new file mode 100644 index 0000000..d8d9eba --- /dev/null +++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java @@ -0,0 +1,81 @@ +/* + * 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.bluetooth; + +import android.content.Context; +import android.test.InstrumentationTestCase; + +public class BluetoothStressTest extends InstrumentationTestCase { + private static final String TAG = "BluetoothStressTest"; + private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt"; + + private BluetoothTestUtils mTestUtils; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + Context context = getInstrumentation().getTargetContext(); + mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + + mTestUtils.close(); + } + + public void testEnable() { + int iterations = BluetoothTestRunner.sEnableIterations; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations); + mTestUtils.enable(adapter); + mTestUtils.disable(adapter); + } + } + + public void testDiscoverable() { + int iterations = BluetoothTestRunner.sDiscoverableIterations; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + mTestUtils.enable(adapter); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations); + mTestUtils.discoverable(adapter); + mTestUtils.undiscoverable(adapter); + } + + mTestUtils.disable(adapter); + } + + public void testScan() { + int iterations = BluetoothTestRunner.sScanIterations; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + mTestUtils.enable(adapter); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations); + mTestUtils.startScan(adapter); + mTestUtils.stopScan(adapter); + } + + mTestUtils.disable(adapter); + } +} diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java new file mode 100644 index 0000000..cf0ff99 --- /dev/null +++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java @@ -0,0 +1,73 @@ +/* + * 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.bluetooth; + +import junit.framework.TestSuite; + +import android.os.Bundle; +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; + +public class BluetoothTestRunner extends InstrumentationTestRunner { + public static int sEnableIterations = 100; + public static int sDiscoverableIterations = 1000; + public static int sScanIterations = 1000; + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(BluetoothStressTest.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return BluetoothTestRunner.class.getClassLoader(); + } + + @Override + public void onCreate(Bundle arguments) { + super.onCreate(arguments); + + String val = arguments.getString("enable_iterations"); + if (val != null) { + try { + sEnableIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("discoverable_iterations"); + if (val != null) { + try { + sDiscoverableIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("scan_iterations"); + if (val != null) { + try { + sScanIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + } +} diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java new file mode 100644 index 0000000..82de509 --- /dev/null +++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java @@ -0,0 +1,441 @@ +/* + * 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.bluetooth; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Environment; +import android.util.Log; + +import junit.framework.Assert; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +public class BluetoothTestUtils extends Assert { + + /** + * Timeout for {@link BluetoothAdapter#disable()} in ms. + */ + private static final int DISABLE_TIMEOUT = 5000; + + /** + * Timeout for {@link BluetoothAdapter#enable()} in ms. + */ + private static final int ENABLE_TIMEOUT = 20000; + + /** + * Timeout for {@link BluetoothAdapter#setScanMode(int)} in ms. + */ + private static final int SET_SCAN_MODE_TIMEOUT = 5000; + + /** + * Timeout for {@link BluetoothAdapter#startDiscovery()} in ms. + */ + private static final int START_DISCOVERY_TIMEOUT = 5000; + + /** + * Timeout for {@link BluetoothAdapter#cancelDiscovery()} in ms. + */ + private static final int CANCEL_DISCOVERY_TIMEOUT = 5000; + + private static final int DISCOVERY_STARTED_FLAG = 1; + private static final int DISCOVERY_FINISHED_FLAG = 1 << 1; + private static final int SCAN_MODE_NONE_FLAG = 1 << 2; + private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3; + private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4; + private static final int STATE_OFF_FLAG = 1 << 5; + private static final int STATE_TURNING_ON_FLAG = 1 << 6; + private static final int STATE_ON_FLAG = 1 << 7; + private static final int STATE_TURNING_OFF_FLAG = 1 << 8; + + /** + * Time between polls in ms. + */ + private static final int POLL_TIME = 100; + + private Context mContext; + + private BufferedWriter mOutputWriter; + + private String mOutputFile; + private String mTag; + + private class BluetoothReceiver extends BroadcastReceiver { + private int mFiredFlags = 0; + + @Override + public void onReceive(Context context, Intent intent) { + synchronized (this) { + if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) { + mFiredFlags |= DISCOVERY_STARTED_FLAG; + } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) { + mFiredFlags |= DISCOVERY_FINISHED_FLAG; + } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) { + int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, + BluetoothAdapter.ERROR); + assertNotSame(mode, BluetoothAdapter.ERROR); + switch (mode) { + case BluetoothAdapter.SCAN_MODE_NONE: + mFiredFlags |= SCAN_MODE_NONE_FLAG; + break; + case BluetoothAdapter.SCAN_MODE_CONNECTABLE: + mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG; + break; + case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: + mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG; + break; + } + } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, + BluetoothAdapter.ERROR); + assertNotSame(state, BluetoothAdapter.ERROR); + switch (state) { + case BluetoothAdapter.STATE_OFF: + mFiredFlags |= STATE_OFF_FLAG; + break; + case BluetoothAdapter.STATE_TURNING_ON: + mFiredFlags |= STATE_TURNING_ON_FLAG; + break; + case BluetoothAdapter.STATE_ON: + mFiredFlags |= STATE_ON_FLAG; + break; + case BluetoothAdapter.STATE_TURNING_OFF: + mFiredFlags |= STATE_TURNING_OFF_FLAG; + break; + } + } + } + } + + public int getFiredFlags() { + synchronized (this) { + return mFiredFlags; + } + } + + public void resetFiredFlags() { + synchronized (this) { + mFiredFlags = 0; + } + } + } + + private BluetoothReceiver mReceiver = new BluetoothReceiver(); + + public BluetoothTestUtils(Context context, String tag) { + this(context, tag, null); + } + + public BluetoothTestUtils(Context context, String tag, String outputFile) { + mContext = context; + mTag = tag; + mOutputFile = outputFile; + + if (mOutputFile == null) { + mOutputWriter = null; + } else { + try { + mOutputWriter = new BufferedWriter(new FileWriter(new File( + Environment.getExternalStorageDirectory(), mOutputFile), true)); + } catch (IOException e) { + Log.w(mTag, "Test output file could not be opened", e); + mOutputWriter = null; + } + } + + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); + filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + mContext.registerReceiver(mReceiver, filter); + } + + public void close() { + mContext.unregisterReceiver(mReceiver); + + if (mOutputWriter != null) { + try { + mOutputWriter.close(); + } catch (IOException e) { + Log.w(mTag, "Test output file could not be closed", e); + } + } + } + + public void enable(BluetoothAdapter adapter) { + int mask = STATE_TURNING_ON_FLAG | STATE_ON_FLAG | SCAN_MODE_CONNECTABLE_FLAG; + mReceiver.resetFiredFlags(); + + int state = adapter.getState(); + switch (state) { + case BluetoothAdapter.STATE_ON: + assertTrue(adapter.isEnabled()); + return; + case BluetoothAdapter.STATE_OFF: + case BluetoothAdapter.STATE_TURNING_OFF: + assertFalse(adapter.isEnabled()); + assertTrue(adapter.enable()); + break; + case BluetoothAdapter.STATE_TURNING_ON: + assertFalse(adapter.isEnabled()); + mask = 0; // Don't check for received intents since we might have missed them. + break; + default: + fail("enable() invalid state: state=" + state); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < ENABLE_TIMEOUT) { + state = adapter.getState(); + if (state == BluetoothAdapter.STATE_ON) { + assertTrue(adapter.isEnabled()); + if ((mReceiver.getFiredFlags() & mask) == mask) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("enable() completed in %d ms", + (System.currentTimeMillis() - s))); + return; + } + } else { + assertFalse(adapter.isEnabled()); + assertEquals(BluetoothAdapter.STATE_TURNING_ON, state); + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", + state, BluetoothAdapter.STATE_ON, firedFlags, mask)); + } + + public void disable(BluetoothAdapter adapter) { + int mask = STATE_TURNING_OFF_FLAG | STATE_OFF_FLAG | SCAN_MODE_NONE_FLAG; + mReceiver.resetFiredFlags(); + + int state = adapter.getState(); + switch (state) { + case BluetoothAdapter.STATE_OFF: + assertFalse(adapter.isEnabled()); + return; + case BluetoothAdapter.STATE_ON: + assertTrue(adapter.isEnabled()); + assertTrue(adapter.disable()); + break; + case BluetoothAdapter.STATE_TURNING_ON: + assertFalse(adapter.isEnabled()); + assertTrue(adapter.disable()); + break; + case BluetoothAdapter.STATE_TURNING_OFF: + assertFalse(adapter.isEnabled()); + mask = 0; // Don't check for received intents since we might have missed them. + break; + default: + fail("disable() invalid state: state=" + state); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < DISABLE_TIMEOUT) { + state = adapter.getState(); + if (state == BluetoothAdapter.STATE_OFF) { + assertFalse(adapter.isEnabled()); + if ((mReceiver.getFiredFlags() & mask) == mask) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("disable() completed in %d ms", + (System.currentTimeMillis() - s))); + return; + } + } else { + assertFalse(adapter.isEnabled()); + assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state); + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", + state, BluetoothAdapter.STATE_OFF, firedFlags, mask)); + } + + public void discoverable(BluetoothAdapter adapter) { + int mask = SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("discoverable() bluetooth not enabled"); + } + + int scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + return; + } + + assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE); + assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) { + scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + if ((mReceiver.getFiredFlags() & mask) == mask) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("discoverable() completed in %d ms", + (System.currentTimeMillis() - s))); + return; + } + } else { + assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE); + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x " + + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, + firedFlags, mask)); + } + + public void undiscoverable(BluetoothAdapter adapter) { + int mask = SCAN_MODE_CONNECTABLE_FLAG; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("undiscoverable() bluetooth not enabled"); + } + + int scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { + return; + } + + assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); + assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE)); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) { + scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { + if ((mReceiver.getFiredFlags() & mask) == mask) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("undiscoverable() completed in %d ms", + (System.currentTimeMillis() - s))); + return; + } + } else { + assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x " + + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags, + mask)); + } + + public void startScan(BluetoothAdapter adapter) { + int mask = DISCOVERY_STARTED_FLAG; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("startScan() bluetooth not enabled"); + } + + if (adapter.isDiscovering()) { + return; + } + + assertTrue(adapter.startDiscovery()); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) { + if (adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("startScan() completed in %d ms", + (System.currentTimeMillis() - s))); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)", + adapter.isDiscovering(), firedFlags, mask)); + } + + public void stopScan(BluetoothAdapter adapter) { + int mask = DISCOVERY_FINISHED_FLAG; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("stopScan() bluetooth not enabled"); + } + + if (!adapter.isDiscovering()) { + return; + } + + // TODO: put assertTrue() around cancelDiscovery() once it starts returning true. + adapter.cancelDiscovery(); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) { + if (!adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("stopScan() completed in %d ms", + (System.currentTimeMillis() - s))); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)", + adapter.isDiscovering(), firedFlags, mask)); + + } + + public void writeOutput(String s) { + Log.i(mTag, s); + if (mOutputWriter == null) { + return; + } + try { + mOutputWriter.write(s + "\n"); + mOutputWriter.flush(); + } catch (IOException e) { + Log.w(mTag, "Could not write to output file", e); + } + } + + private void sleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + } + } +} diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h index 9af5871..9a09586 100644 --- a/include/media/stagefright/AudioPlayer.h +++ b/include/media/stagefright/AudioPlayer.h @@ -86,6 +86,10 @@ private: bool mStarted; + bool mIsFirstBuffer; + status_t mFirstBufferResult; + MediaBuffer *mFirstBuffer; + sp<MediaPlayerBase::AudioSink> mAudioSink; static void AudioCallback(int event, void *user, void *info); diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 5c278d9..08fc782 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -16,6 +16,7 @@ package android.media; +import java.util.NoSuchElementException; import android.app.ActivityManagerNative; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -1016,7 +1017,11 @@ public class AudioService extends IAudioService.Stub { } else { mStartcount--; if (mStartcount == 0) { - mCb.unlinkToDeath(this, 0); + try { + mCb.unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "decCount() going to 0 but not registered to binder"); + } } requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED); } @@ -1025,8 +1030,14 @@ public class AudioService extends IAudioService.Stub { public void clearCount(boolean stopSco) { synchronized(mScoClients) { + if (mStartcount != 0) { + try { + mCb.unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder"); + } + } mStartcount = 0; - mCb.unlinkToDeath(this, 0); if (stopSco) { requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED); } diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index bcf2463..c27cfc8 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -23,6 +23,7 @@ #include <media/stagefright/AudioPlayer.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> @@ -41,6 +42,9 @@ AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink) mReachedEOS(false), mFinalStatus(OK), mStarted(false), + mIsFirstBuffer(false), + mFirstBufferResult(OK), + mFirstBuffer(NULL), mAudioSink(audioSink) { } @@ -68,6 +72,24 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { } } + // We allow an optional INFO_FORMAT_CHANGED at the very beginning + // of playback, if there is one, getFormat below will retrieve the + // updated format, if there isn't, we'll stash away the valid buffer + // of data to be used on the first audio callback. + + CHECK(mFirstBuffer == NULL); + + mFirstBufferResult = mSource->read(&mFirstBuffer); + if (mFirstBufferResult == INFO_FORMAT_CHANGED) { + LOGV("INFO_FORMAT_CHANGED!!!"); + + CHECK(mFirstBuffer == NULL); + mFirstBufferResult = OK; + mIsFirstBuffer = false; + } else { + mIsFirstBuffer = true; + } + sp<MetaData> format = mSource->getFormat(); const char *mime; bool success = format->findCString(kKeyMIMEType, &mime); @@ -87,7 +109,14 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { DEFAULT_AUDIOSINK_BUFFERCOUNT, &AudioPlayer::AudioSinkCallback, this); if (err != OK) { - mSource->stop(); + if (mFirstBuffer != NULL) { + mFirstBuffer->release(); + mFirstBuffer = NULL; + } + + if (!sourceAlreadyStarted) { + mSource->stop(); + } return err; } @@ -108,7 +137,14 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { delete mAudioTrack; mAudioTrack = NULL; - mSource->stop(); + if (mFirstBuffer != NULL) { + mFirstBuffer->release(); + mFirstBuffer = NULL; + } + + if (!sourceAlreadyStarted) { + mSource->stop(); + } return err; } @@ -159,6 +195,12 @@ void AudioPlayer::stop() { // Make sure to release any buffer we hold onto so that the // source is able to stop(). + + if (mFirstBuffer != NULL) { + mFirstBuffer->release(); + mFirstBuffer = NULL; + } + if (mInputBuffer != NULL) { LOGV("AudioPlayer releasing input buffer."); @@ -243,6 +285,14 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { Mutex::Autolock autoLock(mLock); if (mSeeking) { + if (mIsFirstBuffer) { + if (mFirstBuffer != NULL) { + mFirstBuffer->release(); + mFirstBuffer = NULL; + } + mIsFirstBuffer = false; + } + options.setSeekTo(mSeekTimeUs); if (mInputBuffer != NULL) { @@ -255,7 +305,17 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { } if (mInputBuffer == NULL) { - status_t err = mSource->read(&mInputBuffer, &options); + status_t err; + + if (mIsFirstBuffer) { + mInputBuffer = mFirstBuffer; + mFirstBuffer = NULL; + err = mFirstBufferResult; + + mIsFirstBuffer = false; + } else { + err = mSource->read(&mInputBuffer, &options); + } CHECK((err == OK && mInputBuffer != NULL) || (err != OK && mInputBuffer == NULL)); diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp index 2bc4448..8ae1135 100644 --- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp +++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp @@ -15,6 +15,7 @@ */ #include "AACDecoder.h" +#define LOG_TAG "AACDecoder" #include "../../include/ESDS.h" @@ -36,26 +37,33 @@ AACDecoder::AACDecoder(const sp<MediaSource> &source) mAnchorTimeUs(0), mNumSamplesOutput(0), mInputBuffer(NULL) { -} -AACDecoder::~AACDecoder() { - if (mStarted) { - stop(); - } + sp<MetaData> srcFormat = mSource->getFormat(); - delete mConfig; - mConfig = NULL; -} + int32_t sampleRate; + CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); -status_t AACDecoder::start(MetaData *params) { - CHECK(!mStarted); + mMeta = new MetaData; + mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer(new MediaBuffer(2048 * 2)); + // We'll always output stereo, regardless of how many channels are + // present in the input due to decoder limitations. + mMeta->setInt32(kKeyChannelCount, 2); + mMeta->setInt32(kKeySampleRate, sampleRate); + int64_t durationUs; + if (srcFormat->findInt64(kKeyDuration, &durationUs)) { + mMeta->setInt64(kKeyDuration, durationUs); + } + mMeta->setCString(kKeyDecoderComponent, "AACDecoder"); + + mInitCheck = initCheck(); +} + +status_t AACDecoder::initCheck() { + memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal)); mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED; - mConfig->aacPlusUpsamplingFactor = 0; - mConfig->aacPlusEnabled = false; + mConfig->aacPlusEnabled = 1; // The software decoder doesn't properly support mono output on // AACplus files. Always output stereo. @@ -64,8 +72,11 @@ status_t AACDecoder::start(MetaData *params) { UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements(); mDecoderBuf = malloc(memRequirements); - CHECK_EQ(PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf), - MP4AUDEC_SUCCESS); + status_t err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf); + if (err != MP4AUDEC_SUCCESS) { + LOGE("Failed to initialize MP4 audio decoder"); + return UNKNOWN_ERROR; + } uint32_t type; const void *data; @@ -83,18 +94,29 @@ status_t AACDecoder::start(MetaData *params) { mConfig->pInputBuffer = (UChar *)codec_specific_data; mConfig->inputBufferCurrentLength = codec_specific_data_size; mConfig->inputBufferMaxLength = 0; - mConfig->inputBufferUsedLength = 0; - mConfig->remainderBits = 0; - - mConfig->pOutputBuffer = NULL; - mConfig->pOutputBuffer_plus = NULL; - mConfig->repositionFlag = false; if (PVMP4AudioDecoderConfig(mConfig, mDecoderBuf) != MP4AUDEC_SUCCESS) { return ERROR_UNSUPPORTED; } } + return OK; +} + +AACDecoder::~AACDecoder() { + if (mStarted) { + stop(); + } + + delete mConfig; + mConfig = NULL; +} + +status_t AACDecoder::start(MetaData *params) { + CHECK(!mStarted); + + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer(new MediaBuffer(4096 * 2)); mSource->start(); @@ -127,28 +149,7 @@ status_t AACDecoder::stop() { } sp<MetaData> AACDecoder::getFormat() { - sp<MetaData> srcFormat = mSource->getFormat(); - - int32_t sampleRate; - CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); - - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - - // We'll always output stereo, regardless of how many channels are - // present in the input due to decoder limitations. - meta->setInt32(kKeyChannelCount, 2); - - meta->setInt32(kKeySampleRate, sampleRate); - - int64_t durationUs; - if (srcFormat->findInt64(kKeyDuration, &durationUs)) { - meta->setInt64(kKeyDuration, durationUs); - } - - meta->setCString(kKeyDecoderComponent, "AACDecoder"); - - return meta; + return mMeta; } status_t AACDecoder::read( @@ -200,13 +201,32 @@ status_t AACDecoder::read( mConfig->remainderBits = 0; mConfig->pOutputBuffer = static_cast<Int16 *>(buffer->data()); - mConfig->pOutputBuffer_plus = NULL; + mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048]; mConfig->repositionFlag = false; Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf); + // Check on the sampling rate to see whether it is changed. + int32_t sampleRate; + CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate)); + if (mConfig->samplingRate != sampleRate) { + mMeta->setInt32(kKeySampleRate, mConfig->samplingRate); + LOGW("Sample rate was %d, but now is %d", + sampleRate, mConfig->samplingRate); + buffer->release(); + mInputBuffer->release(); + mInputBuffer = NULL; + return INFO_FORMAT_CHANGED; + } + size_t numOutBytes = mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels; + if (mConfig->aacPlusUpsamplingFactor == 2) { + if (mConfig->desiredChannels == 1) { + memcpy(&mConfig->pOutputBuffer[1024], &mConfig->pOutputBuffer[2048], numOutBytes * 2); + } + numOutBytes *= 2; + } if (decoderErr != MP4AUDEC_SUCCESS) { LOGW("AAC decoder returned error %d, substituting silence", decoderErr); diff --git a/media/libstagefright/include/AACDecoder.h b/media/libstagefright/include/AACDecoder.h index f09addd..200f93c 100644 --- a/media/libstagefright/include/AACDecoder.h +++ b/media/libstagefright/include/AACDecoder.h @@ -25,6 +25,7 @@ struct tPVMP4AudioDecoderExternal; namespace android { struct MediaBufferGroup; +struct MetaData; struct AACDecoder : public MediaSource { AACDecoder(const sp<MediaSource> &source); @@ -41,6 +42,7 @@ protected: virtual ~AACDecoder(); private: + sp<MetaData> mMeta; sp<MediaSource> mSource; bool mStarted; @@ -50,9 +52,11 @@ private: void *mDecoderBuf; int64_t mAnchorTimeUs; int64_t mNumSamplesOutput; + status_t mInitCheck; MediaBuffer *mInputBuffer; + status_t initCheck(); AACDecoder(const AACDecoder &); AACDecoder &operator=(const AACDecoder &); }; diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 185d72a..8349fe6 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -47,7 +47,8 @@ <bool name="def_networks_available_notification_on">true</bool> <bool name="def_backup_enabled">false</bool> - <string name="def_backup_transport" translatable="false"></string> + <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string> + <!-- Default value for whether or not to pulse the notification LED when there is a pending notification --> <bool name="def_notification_pulse">true</bool> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 2b4714d..dab7601 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -49,6 +49,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.HashSet; import java.util.List; /** @@ -67,11 +68,29 @@ public class DatabaseHelper extends SQLiteOpenHelper { private Context mContext; + private static final HashSet<String> mValidTables = new HashSet<String>(); + + static { + mValidTables.add("system"); + mValidTables.add("secure"); + mValidTables.add("bluetooth_devices"); + mValidTables.add("bookmarks"); + + // These are old. + mValidTables.add("favorites"); + mValidTables.add("gservices"); + mValidTables.add("old_favorites"); + } + public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); mContext = context; } + public static boolean isValidTable(String name) { + return mValidTables.contains(name); + } + private void createSecureTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE secure (" + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index c4b4c31..b8c4961 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -84,6 +84,9 @@ public class SettingsProvider extends ContentProvider { SqlArguments(Uri url, String where, String[] args) { if (url.getPathSegments().size() == 1) { this.table = url.getPathSegments().get(0); + if (!DatabaseHelper.isValidTable(this.table)) { + throw new IllegalArgumentException("Bad root path: " + this.table); + } this.where = where; this.args = args; } else if (url.getPathSegments().size() != 2) { @@ -92,6 +95,9 @@ public class SettingsProvider extends ContentProvider { throw new UnsupportedOperationException("WHERE clause not supported: " + url); } else { this.table = url.getPathSegments().get(0); + if (!DatabaseHelper.isValidTable(this.table)) { + throw new IllegalArgumentException("Bad root path: " + this.table); + } if ("system".equals(this.table) || "secure".equals(this.table)) { this.where = Settings.NameValueTable.NAME + "=?"; this.args = new String[] { url.getPathSegments().get(1) }; @@ -106,6 +112,9 @@ public class SettingsProvider extends ContentProvider { SqlArguments(Uri url) { if (url.getPathSegments().size() == 1) { this.table = url.getPathSegments().get(0); + if (!DatabaseHelper.isValidTable(this.table)) { + throw new IllegalArgumentException("Bad root path: " + this.table); + } this.where = null; this.args = null; } else { @@ -214,16 +223,11 @@ public class SettingsProvider extends ContentProvider { final String value = c.moveToNext() ? c.getString(0) : null; if (value == null) { final SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); - String serial = SystemProperties.get("ro.serialno"); - if (serial != null) { - try { - random.setSeed(serial.getBytes("UTF-8")); - } catch (UnsupportedEncodingException ignore) { - // stick with default seed - } - } + String serial = SystemProperties.get("ro.serialno", ""); + random.setSeed( + (serial + System.nanoTime() + new SecureRandom().nextLong()).getBytes()); final String newAndroidIdValue = Long.toHexString(random.nextLong()); - Log.d(TAG, "Generated and saved new ANDROID_ID"); + Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]"); final ContentValues values = new ContentValues(); values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID); values.put(Settings.NameValueTable.VALUE, newAndroidIdValue); diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 6ceeb95..6c2f1b2 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -642,10 +642,21 @@ class MountService extends IMountService.Stub } private boolean doGetShareMethodAvailable(String method) { - ArrayList<String> rsp = mConnector.doCommand("share status " + method); + ArrayList<String> rsp; + try { + rsp = mConnector.doCommand("share status " + method); + } catch (NativeDaemonConnectorException ex) { + Slog.e(TAG, "Failed to determine whether share method " + method + " is available."); + return false; + } for (String line : rsp) { - String []tok = line.split(" "); + String[] tok = line.split(" "); + if (tok.length < 3) { + Slog.e(TAG, "Malformed response to share status " + method); + return false; + } + int code; try { code = Integer.parseInt(tok[0]); @@ -770,10 +781,22 @@ class MountService extends IMountService.Stub private boolean doGetVolumeShared(String path, String method) { String cmd = String.format("volume shared %s %s", path, method); - ArrayList<String> rsp = mConnector.doCommand(cmd); + ArrayList<String> rsp; + + try { + rsp = mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException ex) { + Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method); + return false; + } for (String line : rsp) { - String []tok = line.split(" "); + String[] tok = line.split(" "); + if (tok.length < 3) { + Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command"); + return false; + } + int code; try { code = Integer.parseInt(tok[0]); @@ -782,9 +805,7 @@ class MountService extends IMountService.Stub return false; } if (code == VoldResponseCode.ShareEnabledResult) { - if (tok[2].equals("enabled")) - return true; - return false; + return "enabled".equals(tok[2]); } else { Slog.e(TAG, String.format("Unexpected response code %d", code)); return false; diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java index 08d7ce6..c452590 100644 --- a/services/java/com/android/server/NativeDaemonConnector.java +++ b/services/java/com/android/server/NativeDaemonConnector.java @@ -128,12 +128,11 @@ final class NativeDaemonConnector implements Runnable { Slog.e(TAG, String.format( "Error handling '%s'", event), ex); } - } else { - try { - mResponseQueue.put(event); - } catch (InterruptedException ex) { - Slog.e(TAG, "Failed to put response onto queue", ex); - } + } + try { + mResponseQueue.put(event); + } catch (InterruptedException ex) { + Slog.e(TAG, "Failed to put response onto queue", ex); } } catch (NumberFormatException nfe) { Slog.w(TAG, String.format("Bad msg (%s)", event)); diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index cbbc7be..c156150 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -35,6 +35,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.Slog; import java.util.ArrayList; +import java.util.NoSuchElementException; import java.util.StringTokenizer; import android.provider.Settings; import android.content.ContentResolver; @@ -226,44 +227,61 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult); + try { + return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Cannot communicate with native daemon to list interfaces"); + } } public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException { - String rsp = mConnector.doCommand("interface getcfg " + iface).get(0); + String rsp; + try { + rsp = mConnector.doCommand("interface getcfg " + iface).get(0); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Cannot communicate with native daemon to get interface config"); + } Slog.d(TAG, String.format("rsp <%s>", rsp)); // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3] StringTokenizer st = new StringTokenizer(rsp); + InterfaceConfiguration cfg; try { - int code = Integer.parseInt(st.nextToken(" ")); - if (code != NetdResponseCode.InterfaceGetCfgResult) { + try { + int code = Integer.parseInt(st.nextToken(" ")); + if (code != NetdResponseCode.InterfaceGetCfgResult) { + throw new IllegalStateException( + String.format("Expected code %d, but got %d", + NetdResponseCode.InterfaceGetCfgResult, code)); + } + } catch (NumberFormatException nfe) { throw new IllegalStateException( - String.format("Expected code %d, but got %d", - NetdResponseCode.InterfaceGetCfgResult, code)); + String.format("Invalid response from daemon (%s)", rsp)); } - } catch (NumberFormatException nfe) { - throw new IllegalStateException( - String.format("Invalid response from daemon (%s)", rsp)); - } - InterfaceConfiguration cfg = new InterfaceConfiguration(); - cfg.hwAddr = st.nextToken(" "); - try { - cfg.ipAddr = stringToIpAddr(st.nextToken(" ")); - } catch (UnknownHostException uhe) { - Slog.e(TAG, "Failed to parse ipaddr", uhe); - cfg.ipAddr = 0; - } + cfg = new InterfaceConfiguration(); + cfg.hwAddr = st.nextToken(" "); + try { + cfg.ipAddr = stringToIpAddr(st.nextToken(" ")); + } catch (UnknownHostException uhe) { + Slog.e(TAG, "Failed to parse ipaddr", uhe); + cfg.ipAddr = 0; + } - try { - cfg.netmask = stringToIpAddr(st.nextToken(" ")); - } catch (UnknownHostException uhe) { - Slog.e(TAG, "Failed to parse netmask", uhe); - cfg.netmask = 0; + try { + cfg.netmask = stringToIpAddr(st.nextToken(" ")); + } catch (UnknownHostException uhe) { + Slog.e(TAG, "Failed to parse netmask", uhe); + cfg.netmask = 0; + } + cfg.interfaceFlags = st.nextToken("]").trim() +"]"; + } catch (NoSuchElementException nsee) { + throw new IllegalStateException( + String.format("Invalid response from daemon (%s)", rsp)); } - cfg.interfaceFlags = st.nextToken("]").trim() +"]"; Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags)); return cfg; } @@ -272,7 +290,12 @@ class NetworkManagementService extends INetworkManagementService.Stub { String iface, InterfaceConfiguration cfg) throws IllegalStateException { String cmd = String.format("interface setcfg %s %s %s %s", iface, intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags); - mConnector.doCommand(cmd); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate with native daemon to interface setcfg"); + } } public void shutdown() { @@ -289,20 +312,25 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - ArrayList<String> rsp = mConnector.doCommand("ipfwd status"); + ArrayList<String> rsp; + try { + rsp = mConnector.doCommand("ipfwd status"); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate with native daemon to ipfwd status"); + } for (String line : rsp) { - String []tok = line.split(" "); + String[] tok = line.split(" "); + if (tok.length < 3) { + Slog.e(TAG, "Malformed response from native daemon: " + line); + return false; + } + int code = Integer.parseInt(tok[0]); if (code == NetdResponseCode.IpFwdStatusResult) { // 211 Forwarding <enabled/disabled> - if (tok.length !=2) { - throw new IllegalStateException( - String.format("Malformatted list entry '%s'", line)); - } - if (tok[2].equals("enabled")) - return true; - return false; + return "enabled".equals(tok[2]); } else { throw new IllegalStateException(String.format("Unexpected response code %d", code)); } @@ -326,29 +354,45 @@ class NetworkManagementService extends INetworkManagementService.Stub { for (String d : dhcpRange) { cmd += " " + d; } - mConnector.doCommand(cmd); + + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Unable to communicate to native daemon"); + } } public void stopTethering() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mConnector.doCommand("tether stop"); + try { + mConnector.doCommand("tether stop"); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Unable to communicate to native daemon to stop tether"); + } } public boolean isTetheringStarted() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - ArrayList<String> rsp = mConnector.doCommand("tether status"); + ArrayList<String> rsp; + try { + rsp = mConnector.doCommand("tether status"); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate to native daemon to get tether status"); + } for (String line : rsp) { - String []tok = line.split(" "); + String[] tok = line.split(" "); + if (tok.length < 3) { + throw new IllegalStateException("Malformed response for tether status: " + line); + } int code = Integer.parseInt(tok[0]); if (code == NetdResponseCode.TetherStatusResult) { // XXX: Tethering services <started/stopped> <TBD>... - if (tok[2].equals("started")) - return true; - return false; + return "started".equals(tok[2]); } else { throw new IllegalStateException(String.format("Unexpected response code %d", code)); } @@ -359,20 +403,35 @@ class NetworkManagementService extends INetworkManagementService.Stub { public void tetherInterface(String iface) throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mConnector.doCommand("tether interface add " + iface); + try { + mConnector.doCommand("tether interface add " + iface); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate to native daemon for adding tether interface"); + } } public void untetherInterface(String iface) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mConnector.doCommand("tether interface remove " + iface); + try { + mConnector.doCommand("tether interface remove " + iface); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate to native daemon for removing tether interface"); + } } public String[] listTetheredInterfaces() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - return mConnector.doListCommand( - "tether interface list", NetdResponseCode.TetherInterfaceListResult); + try { + return mConnector.doListCommand( + "tether interface list", NetdResponseCode.TetherInterfaceListResult); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate to native daemon for listing tether interfaces"); + } } public void setDnsForwarders(String[] dns) throws IllegalStateException { @@ -383,7 +442,12 @@ class NetworkManagementService extends INetworkManagementService.Stub { for (String s : dns) { cmd += " " + InetAddress.getByName(s).getHostAddress(); } - mConnector.doCommand(cmd); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate to native daemon for setting tether dns"); + } } catch (UnknownHostException e) { throw new IllegalStateException("Error resolving dns name", e); } @@ -392,30 +456,50 @@ class NetworkManagementService extends INetworkManagementService.Stub { public String[] getDnsForwarders() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - return mConnector.doListCommand( - "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); + try { + return mConnector.doListCommand( + "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate to native daemon for listing tether dns"); + } } public void enableNat(String internalInterface, String externalInterface) throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mConnector.doCommand( - String.format("nat enable %s %s", internalInterface, externalInterface)); + try { + mConnector.doCommand( + String.format("nat enable %s %s", internalInterface, externalInterface)); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate to native daemon for enabling NAT interface"); + } } public void disableNat(String internalInterface, String externalInterface) throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mConnector.doCommand( - String.format("nat disable %s %s", internalInterface, externalInterface)); + try { + mConnector.doCommand( + String.format("nat disable %s %s", internalInterface, externalInterface)); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate to native daemon for disabling NAT interface"); + } } public String[] listTtys() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult); + try { + return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Unable to communicate to native daemon for listing TTYs"); + } } public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr, @@ -430,31 +514,52 @@ class NetworkManagementService extends INetworkManagementService.Stub { InetAddress.getByName(dns2Addr).getHostAddress())); } catch (UnknownHostException e) { throw new IllegalStateException("Error resolving addr", e); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon to attach pppd", e); } } public void detachPppd(String tty) throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mConnector.doCommand(String.format("pppd detach %s", tty)); + try { + mConnector.doCommand(String.format("pppd detach %s", tty)); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon to detach pppd", e); + } } public void startUsbRNDIS() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mConnector.doCommand("usb startrndis"); + try { + mConnector.doCommand("usb startrndis"); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Error communicating to native daemon for starting RNDIS", e); + } } public void stopUsbRNDIS() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mConnector.doCommand("usb stoprndis"); + try { + mConnector.doCommand("usb stoprndis"); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } } public boolean isUsbRNDISStarted() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus"); + ArrayList<String> rsp; + try { + rsp = mConnector.doCommand("usb rndisstatus"); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Error communicating to native daemon to check RNDIS status", e); + } for (String line : rsp) { String []tok = line.split(" "); @@ -476,31 +581,35 @@ class NetworkManagementService extends INetworkManagementService.Stub { android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); - mConnector.doCommand(String.format("softap stop " + wlanIface)); - mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP")); - mConnector.doCommand(String.format("softap start " + wlanIface)); - if (wifiConfig == null) { - mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); - } else { - /** - * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8] - * argv1 - wlan interface - * argv2 - softap interface - * argv3 - SSID - * argv4 - Security - * argv5 - Key - * argv6 - Channel - * argv7 - Preamble - * argv8 - Max SCB - */ - String str = String.format("softap set " + wlanIface + " " + softapIface + - " %s %s %s", convertQuotedString(wifiConfig.SSID), - wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? - "wpa2-psk" : "open", - convertQuotedString(wifiConfig.preSharedKey)); - mConnector.doCommand(str); - } - mConnector.doCommand(String.format("softap startap")); + try { + mConnector.doCommand(String.format("softap stop " + wlanIface)); + mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP")); + mConnector.doCommand(String.format("softap start " + wlanIface)); + if (wifiConfig == null) { + mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); + } else { + /** + * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8] + * argv1 - wlan interface + * argv2 - softap interface + * argv3 - SSID + * argv4 - Security + * argv5 - Key + * argv6 - Channel + * argv7 - Preamble + * argv8 - Max SCB + */ + String str = String.format("softap set " + wlanIface + " " + softapIface + + " %s %s %s", convertQuotedString(wifiConfig.SSID), + wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? + "wpa2-psk" : "open", + convertQuotedString(wifiConfig.preSharedKey)); + mConnector.doCommand(str); + } + mConnector.doCommand(String.format("softap startap")); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon to start softap", e); + } } private String convertQuotedString(String s) { @@ -516,7 +625,12 @@ class NetworkManagementService extends INetworkManagementService.Stub { android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); - mConnector.doCommand("softap stopap"); + try { + mConnector.doCommand("softap stopap"); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon to stop soft AP", + e); + } } public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) @@ -525,15 +639,19 @@ class NetworkManagementService extends INetworkManagementService.Stub { android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); - if (wifiConfig == null) { - mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); - } else { - String str = String.format("softap set " + wlanIface + " " + softapIface + - " %s %s %s", convertQuotedString(wifiConfig.SSID), - wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? - "wpa2-psk" : "open", - convertQuotedString(wifiConfig.preSharedKey)); - mConnector.doCommand(str); + try { + if (wifiConfig == null) { + mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); + } else { + String str = String.format("softap set " + wlanIface + " " + softapIface + + " %s %s %s", convertQuotedString(wifiConfig.SSID), + wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? "wpa2-psk" : "open", + convertQuotedString(wifiConfig.preSharedKey)); + mConnector.doCommand(str); + } + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon to set soft AP", + e); } } @@ -541,9 +659,22 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); try { - String rsp = mConnector.doCommand( - String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0); - String []tok = rsp.split(" "); + String rsp; + try { + rsp = mConnector.doCommand( + String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0); + } catch (NativeDaemonConnectorException e1) { + Slog.e(TAG, "Error communicating with native daemon", e1); + return -1; + } + + String[] tok = rsp.split(" "); + if (tok.length < 2) { + Slog.e(TAG, String.format("Malformed response for reading %s interface", + (rx ? "rx" : "tx"))); + return -1; + } + int code; try { code = Integer.parseInt(tok[0]); @@ -575,17 +706,34 @@ class NetworkManagementService extends INetworkManagementService.Stub { public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mConnector.doCommand(String.format( - "interface setthrottle %s %d %d", iface, rxKbps, txKbps)); + try { + mConnector.doCommand(String.format( + "interface setthrottle %s %d %d", iface, rxKbps, txKbps)); + } catch (NativeDaemonConnectorException e) { + Slog.e(TAG, "Error communicating with native daemon to set throttle", e); + } } private int getInterfaceThrottle(String iface, boolean rx) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); try { - String rsp = mConnector.doCommand( - String.format("interface getthrottle %s %s", iface,(rx ? "rx" : "tx"))).get(0); - String []tok = rsp.split(" "); + String rsp; + try { + rsp = mConnector.doCommand( + String.format("interface getthrottle %s %s", iface, + (rx ? "rx" : "tx"))).get(0); + } catch (NativeDaemonConnectorException e) { + Slog.e(TAG, "Error communicating with native daemon to getthrottle", e); + return -1; + } + + String[] tok = rsp.split(" "); + if (tok.length < 2) { + Slog.e(TAG, "Malformed response to getthrottle command"); + return -1; + } + int code; try { code = Integer.parseInt(tok[0]); diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 0931cc0..c2ec774 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -3654,21 +3654,19 @@ class PackageManagerService extends IPackageManager.Stub { installedNativeLibraries = true; + // Always extract the shared library String sharedLibraryFilePath = sharedLibraryDir.getPath() + File.separator + libFileName; File sharedLibraryFile = new File(sharedLibraryFilePath); - if (! sharedLibraryFile.exists() || - sharedLibraryFile.length() != entry.getSize() || - sharedLibraryFile.lastModified() != entry.getTime()) { - if (Config.LOGD) { - Log.d(TAG, "Caching shared lib " + entry.getName()); - } - if (mInstaller == null) { - sharedLibraryDir.mkdir(); - } - cacheNativeBinaryLI(pkg, zipFile, entry, sharedLibraryDir, - sharedLibraryFile); + + if (Config.LOGD) { + Log.d(TAG, "Caching shared lib " + entry.getName()); } + if (mInstaller == null) { + sharedLibraryDir.mkdir(); + } + cacheNativeBinaryLI(pkg, zipFile, entry, sharedLibraryDir, + sharedLibraryFile); } if (!hasNativeLibraries) return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES; @@ -3710,18 +3708,16 @@ class PackageManagerService extends IPackageManager.Stub { String installGdbServerPath = installGdbServerDir.getPath() + "/" + GDBSERVER; File installGdbServerFile = new File(installGdbServerPath); - if (! installGdbServerFile.exists() || - installGdbServerFile.length() != entry.getSize() || - installGdbServerFile.lastModified() != entry.getTime()) { - if (Config.LOGD) { - Log.d(TAG, "Caching gdbserver " + entry.getName()); - } - if (mInstaller == null) { - installGdbServerDir.mkdir(); - } - cacheNativeBinaryLI(pkg, zipFile, entry, installGdbServerDir, - installGdbServerFile); + + if (Config.LOGD) { + Log.d(TAG, "Caching gdbserver " + entry.getName()); + } + if (mInstaller == null) { + installGdbServerDir.mkdir(); } + cacheNativeBinaryLI(pkg, zipFile, entry, installGdbServerDir, + installGdbServerFile); + return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES; } return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES; @@ -3735,6 +3731,16 @@ class PackageManagerService extends IPackageManager.Stub { // one if ro.product.cpu.abi2 is defined. // private int cachePackageSharedLibsLI(PackageParser.Package pkg, File scanFile) { + // Remove all native binaries from a directory. This is used when upgrading + // a package: in case the new .apk doesn't contain a native binary that was + // in the old one (and thus installed), we need to remove it from + // /data/data/<appname>/lib + // + // The simplest way to do that is to remove all files in this directory, + // since it is owned by "system", applications are not supposed to write + // anything there. + removeNativeBinariesLI(pkg); + String cpuAbi = Build.CPU_ABI; try { int result = cachePackageSharedLibsForAbiLI(pkg, scanFile, cpuAbi); @@ -6439,11 +6445,10 @@ class PackageManagerService extends IPackageManager.Stub { File dataDir = new File(pkg.applicationInfo.dataDir); dataDir.delete(); } + schedulePackageCleaning(packageName); } synchronized (mPackages) { if (deletedPs != null) { - schedulePackageCleaning(packageName); - if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { if (outInfo != null) { outInfo.removedUid = mSettings.removePackageLP(packageName); diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 493a348..55f9d60 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -1022,36 +1022,76 @@ class PowerManagerService extends IPowerManager.Stub } } - private void setTimeoutLocked(long now, int nextState) - { + private void setTimeoutLocked(long now, int nextState) { + setTimeoutLocked(now, -1, nextState); + } + + // If they gave a timeoutOverride it is the number of seconds + // to screen-off. Figure out where in the countdown cycle we + // should jump to. + private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) { + long timeoutOverride = originalTimeoutOverride; if (mBootCompleted) { - mHandler.removeCallbacks(mTimeoutTask); - mTimeoutTask.nextState = nextState; - long when = now; - switch (nextState) - { - case SCREEN_BRIGHT: - when += mKeylightDelay; - break; - case SCREEN_DIM: - if (mDimDelay >= 0) { - when += mDimDelay; - break; - } else { - Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim"); + synchronized (mLocks) { + long when = 0; + if (timeoutOverride <= 0) { + switch (nextState) + { + case SCREEN_BRIGHT: + when = now + mKeylightDelay; + break; + case SCREEN_DIM: + if (mDimDelay >= 0) { + when = now + mDimDelay; + break; + } else { + Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim"); + } + case SCREEN_OFF: + synchronized (mLocks) { + when = now + mScreenOffDelay; + } + break; + default: + when = now; + break; } - case SCREEN_OFF: - synchronized (mLocks) { - when += mScreenOffDelay; + } else { + override: { + if (timeoutOverride <= mScreenOffDelay) { + when = now + timeoutOverride; + nextState = SCREEN_OFF; + break override; + } + timeoutOverride -= mScreenOffDelay; + + if (mDimDelay >= 0) { + if (timeoutOverride <= mDimDelay) { + when = now + timeoutOverride; + nextState = SCREEN_DIM; + break override; + } + timeoutOverride -= mDimDelay; + } + + when = now + timeoutOverride; + nextState = SCREEN_BRIGHT; } - break; - } - if (mSpew) { - Slog.d(TAG, "setTimeoutLocked now=" + now + " nextState=" + nextState - + " when=" + when); + } + if (mSpew) { + Slog.d(TAG, "setTimeoutLocked now=" + now + + " timeoutOverride=" + timeoutOverride + + " nextState=" + nextState + " when=" + when); + } + + mHandler.removeCallbacks(mTimeoutTask); + mTimeoutTask.nextState = nextState; + mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0 + ? (originalTimeoutOverride - timeoutOverride) + : -1; + mHandler.postAtTime(mTimeoutTask, when); + mNextTimeout = when; // for debugging } - mHandler.postAtTime(mTimeoutTask, when); - mNextTimeout = when; // for debugging } } @@ -1064,6 +1104,7 @@ class PowerManagerService extends IPowerManager.Stub private class TimeoutTask implements Runnable { int nextState; // access should be synchronized on mLocks + long remainingTimeoutOverride; public void run() { synchronized (mLocks) { @@ -1084,11 +1125,11 @@ class PowerManagerService extends IPowerManager.Stub { case SCREEN_BRIGHT: if (mDimDelay >= 0) { - setTimeoutLocked(now, SCREEN_DIM); + setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM); break; } case SCREEN_DIM: - setTimeoutLocked(now, SCREEN_OFF); + setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF); break; } } @@ -1958,18 +1999,33 @@ class PowerManagerService extends IPowerManager.Stub public void userActivityWithForce(long time, boolean noChangeLights, boolean force) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - userActivity(time, noChangeLights, OTHER_EVENT, force); + userActivity(time, -1, noChangeLights, OTHER_EVENT, force); } public void userActivity(long time, boolean noChangeLights) { - userActivity(time, noChangeLights, OTHER_EVENT, false); + userActivity(time, -1, noChangeLights, OTHER_EVENT, false); } public void userActivity(long time, boolean noChangeLights, int eventType) { - userActivity(time, noChangeLights, eventType, false); + userActivity(time, -1, noChangeLights, eventType, false); } public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) { + userActivity(time, -1, noChangeLights, eventType, force); + } + + /* + * Reset the user activity timeout to now + timeout. This overrides whatever else is going + * on with user activity. Don't use this function. + */ + public void clearUserActivityTimeout(long now, long timeout) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + Slog.i(TAG, "clearUserActivity for " + timeout + "ms from now"); + userActivity(now, timeout, false, OTHER_EVENT, false); + } + + private void userActivity(long time, long timeoutOverride, boolean noChangeLights, + int eventType, boolean force) { //mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0) @@ -2004,6 +2060,7 @@ class PowerManagerService extends IPowerManager.Stub + " mUserState=0x" + Integer.toHexString(mUserState) + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState) + " mProximitySensorActive=" + mProximitySensorActive + + " timeoutOverride=" + timeoutOverride + " force=" + force); } // ignore user activity if we are in the process of turning off the screen @@ -2041,7 +2098,7 @@ class PowerManagerService extends IPowerManager.Stub mWakeLockState = mLocks.reactivateScreenLocksLocked(); setPowerState(mUserState | mWakeLockState, noChangeLights, WindowManagerPolicy.OFF_BECAUSE_OF_USER); - setTimeoutLocked(time, SCREEN_BRIGHT); + setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT); } } } diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 32e7176..55d25a5 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -135,9 +135,9 @@ public class PhoneNumberUtils } // TODO: We don't check for SecurityException here (requires - // READ_PHONE_STATE permission). + // CALL_PRIVILEGED permission). if (scheme.equals("voicemail")) { - return TelephonyManager.getDefault().getVoiceMailNumber(); + return TelephonyManager.getDefault().getCompleteVoiceMailNumber(); } if (context == null) { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f018d10..278e8ca 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -177,7 +177,7 @@ public class TelephonyManager { /** * Returns the unique device ID, for example, the IMEI for GSM and the MEID - * for CDMA phones. Return null if device ID is not available. + * or ESN for CDMA phones. Return null if device ID is not available. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -659,6 +659,25 @@ public class TelephonyManager { } /** + * Returns the complete voice mail number. Return null if it is unavailable. + * <p> + * Requires Permission: + * {@link android.Manifest.permission#CALL_PRIVILEGED CALL_PRIVILEGED} + * + * @hide + */ + public String getCompleteVoiceMailNumber() { + try { + return getSubscriberInfo().getCompleteVoiceMailNumber(); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** * Returns the voice mail count. Return 0 if unavailable. * <p> * Requires Permission: diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl index e74b9e4..5cba2e1 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl @@ -59,6 +59,11 @@ interface IPhoneSubInfo { String getVoiceMailNumber(); /** + * Retrieves the complete voice mail number. + */ + String getCompleteVoiceMailNumber(); + + /** * Retrieves the alpha identifier associated with the voice mail number. */ String getVoiceMailAlphaTag(); diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java index 1ac2da3..0b55736 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java @@ -21,6 +21,7 @@ import java.io.PrintWriter; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; +import android.telephony.PhoneNumberUtils; import android.util.Log; public class PhoneSubInfo extends IPhoneSubInfo.Stub { @@ -29,6 +30,9 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { private Context mContext; private static final String READ_PHONE_STATE = android.Manifest.permission.READ_PHONE_STATE; + private static final String CALL_PRIVILEGED = + // TODO Add core/res/AndriodManifest.xml#READ_PRIVILEGED_PHONE_STATE + android.Manifest.permission.CALL_PRIVILEGED; public PhoneSubInfo(Phone phone) { mPhone = phone; @@ -101,7 +105,22 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { */ public String getVoiceMailNumber() { mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE"); - return (String) mPhone.getVoiceMailNumber(); + String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber()); + Log.d(LOG_TAG, "VM: PhoneSubInfo.getVoiceMailNUmber: "); // + number); + return number; + } + + /** + * Retrieves the compelete voice mail number. + * + * @hide + */ + public String getCompleteVoiceMailNumber() { + mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED, + "Requires CALL_PRIVILEGED"); + String number = mPhone.getVoiceMailNumber(); + Log.d(LOG_TAG, "VM: PhoneSubInfo.getCompleteVoiceMailNUmber: "); // + number); + return number; } /** diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java index adfbe20..a036046 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java @@ -82,6 +82,13 @@ public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub { } /** + * Retrieves the complete voice mail number. + */ + public String getCompleteVoiceMailNumber() { + return mPhoneSubInfo.getCompleteVoiceMailNumber(); + } + + /** * Retrieves the alpha identifier associated with the voice mail number. */ public String getVoiceMailAlphaTag() { diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 1f5accf..58dfeb9 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -430,9 +430,14 @@ public class CDMAPhone extends PhoneBase { return mMeid; } - //returns MEID in CDMA + //returns MEID or ESN in CDMA public String getDeviceId() { - return getMeid(); + String id = getMeid(); + if ((id == null) || id.matches("^0*$")) { + Log.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN"); + id = getEsn(); + } + return id; } public String getDeviceSvn() { |