Merge "Fix CTS: ViewGroupTests failing" into mnc-dev
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index d22f5ec..fa4203d 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1523,6 +1523,10 @@
     Overlay view must be shown. Verify that there is a text view displaying \"Overlay View Dummy Text\"
     when you tune to the \"Dummy\" channel.
     </string>
+    <string name="tv_input_discover_test_verify_global_search">
+    Live Channels app should provide query results for 3rd-party input\'s channels and programs on
+    global search requests.
+    </string>
     <string name="tv_input_discover_test_go_to_epg">
     Press the \"Launch EPG\" button, and locate the channel named \"Dummy\".
     </string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
index 0a0e830..48ce03c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
@@ -547,7 +547,18 @@
                 break;
             }
         }
-        // Second try to find one with similar if not the same aspect ratio
+        // Second try to find same ratio in size
+        if (matchedSize == null) {
+            for (int i = mPreviewSizes.size() - 1; i >= 0; i--) {
+                if (mPreviewSizes.get(i).width * recordSize.height ==
+                        mPreviewSizes.get(i).height * recordSize.width) {
+                    matchedSize = mCamera.new Size(mPreviewSizes.get(i).width,
+                            mPreviewSizes.get(i).height);
+                    break;
+                }
+            }
+        }
+        //Third try to find one with similar if not the same apect ratio
         if (matchedSize == null) {
             for (int i = mPreviewSizes.size() - 1; i >= 0; i--) {
                 if (Math.abs((float)mPreviewSizes.get(i).width * recordSize.height /
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
old mode 100644
new mode 100755
index e97539d..8d10bda
--- a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
@@ -31,6 +31,8 @@
             ConnectivityConstraintTestActivity.class.hashCode() + 1;
     private static final int NO_CONNECTIVITY_JOB_ID =
             ConnectivityConstraintTestActivity.class.hashCode() + 2;
+    private static final int NO_CONNECTIVITY_JOB_ID_2 =
+            ConnectivityConstraintTestActivity.class.hashCode() + 3;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -89,18 +91,25 @@
     }
 
     private void testNoConnectivityConstraintExecutes_noConnectivity() {
-        JobInfo testJob = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID, mMockComponent)
+        JobInfo testJob1 = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID, mMockComponent)
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
                 .setOverrideDeadline(100000L)  // Will not expire.
                 .build();
+        JobInfo testJob2 = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID_2, mMockComponent)
+        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
+        .setOverrideDeadline(100000L)  // Will not expire.
+        .build();
 
         mTestEnvironment.setUp();
-        mTestEnvironment.setExpectedExecutions(1);
+        mTestEnvironment.setExpectedExecutions(2);
 
-        mJobScheduler.schedule(testJob);
+        mJobScheduler.schedule(testJob1);
+        mJobScheduler.schedule(testJob2);
 
+        /*
         // Send intent to kick off ready jobs that the JobScheduler might be lazily holding on to.
         sendBroadcastAndBlockForResult(EXPEDITE_STABLE_CHARGING);
+        */
 
         boolean testPassed;
         try {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
index 1d3fd40..c05b753 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
@@ -33,11 +33,11 @@
 public class MockTvInputSetupActivity extends Activity {
     private static final String TAG = "MockTvInputSetupActivity";
 
-    private static final String CHANNEL_NUMBER = "999-0";
-    private static final String CHANNEL_NAME = "Dummy";
+    /* package-private */ static final String CHANNEL_NUMBER = "999-0";
+    /* package-private */ static final String CHANNEL_NAME = "Dummy";
 
-    private static final String PROGRAM_TITLE = "Dummy Program";
-    private static final String PROGRAM_DESCRIPTION = "Dummy Program Description";
+    /* package-private */ static final String PROGRAM_TITLE = "Dummy Program";
+    /* package-private */ static final String PROGRAM_DESCRIPTION = "Dummy Program Description";
     private static final long PROGRAM_LENGTH_MILLIS = 60 * 60 * 1000;
     private static final int PROGRAM_COUNT = 24;
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/SearchUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/SearchUtil.java
new file mode 100644
index 0000000..4513123
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/SearchUtil.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 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.cts.verifier.tv;
+
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A utility class for verifying channel/program results on global search requests.
+ */
+public class SearchUtil {
+    private SearchUtil() {}
+
+    /**
+     * Returns {@code true} if one of the search results matches the given {@code expectedResult}.
+     *
+     * @param context The context object to be used for getting content resolver
+     * @param searchable The {@link android.app.SearchableInfo} the TV app implements
+     * @param query A query string to search for
+     * @param expectedResult The expected search result
+     */
+    public static boolean verifySearchResult(Context context, SearchableInfo searchable,
+            String query, String expectedResult) {
+        Uri.Builder uriBuilder = getSearchUri(searchable).buildUpon();
+        String selection = searchable.getSuggestSelection();
+        String[] selectionArg = null;
+        if (selection != null) {
+            selectionArg = new String[] { query };
+        } else {
+            uriBuilder.appendPath(query);
+        }
+
+        Uri uri = uriBuilder.build();
+        ContentProviderClient provider = context.getContentResolver()
+                .acquireUnstableContentProviderClient(uri);
+        try (Cursor c = provider.query(uri, null, selection, selectionArg, null, null)) {
+            while (c.moveToNext()) {
+                int index = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
+                if (index >= 0) {
+                    if (TextUtils.equals(expectedResult, c.getString(index))) {
+                        return true;
+                    }
+                }
+            }
+        } catch (SQLiteException | RemoteException e) {
+            return false;
+        } finally {
+            provider.release();
+        }
+        return false;
+    }
+
+    /**
+     * Returns the {@link android.app.SearchableInfo} instances which should provide search results
+     * for channels and programs in TvProvider.
+     *
+     * @param context The context object to used for accessing system services
+     */
+    public static List<SearchableInfo> getSearchableInfos(Context context) {
+        // Just in case EPG is provided by a separate package, collect all possible TV packages
+        // that can be searchable.
+        PackageManager pm = context.getPackageManager();
+        Set<String> tvPackages = new HashSet<>();
+        List<ResolveInfo> infos = pm.queryIntentActivities(new Intent(Intent.ACTION_VIEW,
+                TvContract.Channels.CONTENT_URI), 0);
+        for (ResolveInfo info : infos) {
+            tvPackages.add(info.activityInfo.packageName);
+        }
+        infos = pm.queryIntentActivities(new Intent(Intent.ACTION_VIEW,
+                TvContract.Programs.CONTENT_URI), 0);
+        for (ResolveInfo info : infos) {
+            tvPackages.add(info.activityInfo.packageName);
+        }
+        SearchManager sm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+        List<SearchableInfo> globalSearchableInfos = sm.getSearchablesInGlobalSearch();
+        List<SearchableInfo> tvSearchableInfos = new ArrayList<>();
+        for (SearchableInfo info : globalSearchableInfos) {
+            if (tvPackages.contains(info.getSearchActivity().getPackageName())) {
+                tvSearchableInfos.add(info);
+            }
+        }
+        return tvSearchableInfos;
+    }
+
+    private static Uri getSearchUri(SearchableInfo searchable) {
+        if (searchable == null) {
+            return null;
+        }
+        String authority = searchable.getSuggestAuthority();
+        if (authority == null) {
+            return null;
+        }
+        Uri.Builder uriBuilder = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority);
+
+        final String contentPath = searchable.getSuggestPath();
+        if (contentPath != null) {
+            uriBuilder.appendEncodedPath(contentPath);
+        }
+
+        uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
+        return uriBuilder.build();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
index 4d12d52..06f4f6f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
@@ -16,12 +16,17 @@
 
 package com.android.cts.verifier.tv;
 
+import android.app.SearchableInfo;
+import android.content.Context;
 import android.content.Intent;
 import android.media.tv.TvContract;
+import android.os.AsyncTask;
 import android.view.View;
 
 import com.android.cts.verifier.R;
 
+import java.util.List;
+
 /**
  * Tests for verifying TV app behavior for third-party TV input apps.
  */
@@ -41,10 +46,12 @@
     private View mTuneToChannelItem;
     private View mVerifyTuneItem;
     private View mVerifyOverlayViewItem;
+    private View mVerifyGlobalSearchItem;
     private View mGoToEpgItem;
     private View mVerifyEpgItem;
     private boolean mTuneVerified;
     private boolean mOverlayViewVerified;
+    private boolean mGlobalSearchVerified;
 
     @Override
     public void onClick(View v) {
@@ -96,6 +103,7 @@
                     goToNextState(postTarget, failCallback);
                 }
             });
+            verifyGlobalSearch(postTarget, failCallback);
             startActivity(TV_APP_INTENT);
         } else if (containsButton(mGoToEpgItem, v)) {
             startActivity(EPG_INTENT);
@@ -118,22 +126,52 @@
         mVerifyTuneItem = createAutoItem(R.string.tv_input_discover_test_verify_tune);
         mVerifyOverlayViewItem = createAutoItem(
                 R.string.tv_input_discover_test_verify_overlay_view);
+        mVerifyGlobalSearchItem = createAutoItem(
+                R.string.tv_input_discover_test_verify_global_search);
         mGoToEpgItem = createUserItem(R.string.tv_input_discover_test_go_to_epg,
                 R.string.tv_launch_epg, this);
         mVerifyEpgItem = createUserItem(R.string.tv_input_discover_test_verify_epg,
                 R.string.tv_input_discover_test_yes, this);
     }
 
+    @Override
+    protected void setInfoResources() {
+        setInfoResources(R.string.tv_input_discover_test,
+                R.string.tv_input_discover_test_info, -1);
+    }
+
     private void goToNextState(View postTarget, Runnable failCallback) {
-        if (mTuneVerified && mOverlayViewVerified) {
+        if (mTuneVerified && mOverlayViewVerified && mGlobalSearchVerified) {
             postTarget.removeCallbacks(failCallback);
             setButtonEnabled(mGoToEpgItem, true);
         }
     }
 
-    @Override
-    protected void setInfoResources() {
-        setInfoResources(R.string.tv_input_discover_test,
-                R.string.tv_input_discover_test_info, -1);
+    private void verifyGlobalSearch(final View postTarget, final Runnable failCallback) {
+        new AsyncTask<Void, Void, Boolean>() {
+            @Override
+            protected Boolean doInBackground(Void... params) {
+                Context context = TvInputDiscoveryTestActivity.this;
+                for (SearchableInfo info : SearchUtil.getSearchableInfos(context)) {
+                    if (SearchUtil.verifySearchResult(context, info,
+                            MockTvInputSetupActivity.CHANNEL_NAME,
+                            MockTvInputSetupActivity.PROGRAM_TITLE)
+                            && SearchUtil.verifySearchResult(context, info,
+                                    MockTvInputSetupActivity.PROGRAM_TITLE,
+                                    MockTvInputSetupActivity.PROGRAM_TITLE)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            protected void onPostExecute(Boolean result) {
+                super.onPostExecute(result);
+                setPassState(mVerifyGlobalSearchItem, result);
+                mGlobalSearchVerified = result;
+                goToNextState(postTarget, failCallback);
+            }
+        }.execute();
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
index a64ceb9..3c9c061 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
@@ -37,12 +37,14 @@
 import java.util.ArrayList;
 
 /**
- * Basic tests for burst capture in RAW10/16.
+ * Basic tests for burst capture in RAW formats.
  */
 public class BurstCaptureRawTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "BurstCaptureRawTest";
     private static final int RAW_FORMATS[] = {
-            ImageFormat.RAW10, ImageFormat.RAW_SENSOR };
+            ImageFormat.RAW10, ImageFormat.RAW12, ImageFormat.RAW_SENSOR };
+    private static final int NONSTALL_RAW_FORMATS[] = {
+        ImageFormat.RAW10, ImageFormat.RAW12 };
     private static final long EXPOSURE_MULTIPLIERS[] = {
             1, 3, 5 };
     private static final int SENSITIVITY_MLTIPLIERS[] = {
@@ -70,7 +72,7 @@
                 openDevice(id);
 
                 ArrayList<Integer> supportedRawList = new ArrayList<Integer>(RAW_FORMATS.length);
-                if (!checkCapability(supportedRawList)) {
+                if (!checkCapability(supportedRawList, RAW_FORMATS)) {
                     Log.i(TAG, "Capability is not supported on camera " + id
                             + ". Skip the test.");
                     continue;
@@ -104,13 +106,13 @@
     public void testMetadataRoundDown() throws Exception {
         Log.i(TAG, "Begin testMetadataRoundDown");
 
-        performTestRoutine(new TestMetaDataRoundDownRoutine());
+        performTestRoutine(new TestMetaDataRoundDownRoutine(), RAW_FORMATS);
 
         Log.i(TAG, "End testMetadataRoundDown");
     }
 
     /**
-     * Manual and Auto setting test in RAW10/16
+     * Manual and Auto setting test in RAW formats
      * <p>
      * Make sure switching between manual and auto setting would not make the capture results out of
      * sync.
@@ -119,18 +121,18 @@
     public void testManualAutoSwitch() throws Exception {
         Log.i(TAG, "Begin testManualAutoSwitch");
 
-        performTestRoutine(new TestManualAutoSwitch());
+        performTestRoutine(new TestManualAutoSwitch(), RAW_FORMATS);
 
         Log.i(TAG, "End testManualAutoSwitch");
     }
 
     /**
-     * Per frame timestamp test in RAW10/16
+     * Per frame timestamp test in non-stalled RAW formats
      */
     public void testTimestamp() throws Exception {
         Log.i(TAG, "Begin testTimestamp");
 
-        performTestRoutine(new TestTimestamp());
+        performTestRoutine(new TestTimestamp(), NONSTALL_RAW_FORMATS);
 
         Log.i(TAG, "End testTimestamp");
     }
@@ -439,7 +441,7 @@
      *
      * @return true if the it is has the capability to execute the test.
      */
-    private boolean checkCapability(ArrayList<Integer> supportedRawList) {
+    private boolean checkCapability(ArrayList<Integer> supportedRawList, int[] testedFormats) {
         // make sure the sensor has manual support
         if (!mStaticInfo.isCapabilitySupported(
                 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)) {
@@ -453,7 +455,7 @@
 
         // check for the RAW support
         supportedRawList.clear();
-        for (int rawFormat : RAW_FORMATS) {
+        for (int rawFormat : testedFormats) {
             if (!config.isOutputSupportedFor(rawFormat)) {
                 continue;
             }
@@ -479,6 +481,8 @@
         switch (format) {
             case ImageFormat.RAW10:
                 return "RAW10";
+            case ImageFormat.RAW12:
+                return "RAW12";
             case ImageFormat.RAW_SENSOR:
                 return "RAW_SENSOR";
         }
@@ -668,14 +672,15 @@
         stopPreview();
     }
 
-    private void performTestRoutine(TestRoutine routine) throws Exception
+    private void performTestRoutine(TestRoutine routine, int[] testedFormats) throws Exception
     {
+        final int PREPARE_TIMEOUT_MS = 10000;
         for (String id : mCameraIds) {
             try {
                 openDevice(id);
 
                 ArrayList<Integer> supportedRawList = new ArrayList<Integer>(RAW_FORMATS.length);
-                if (!checkCapability(supportedRawList)) {
+                if (!checkCapability(supportedRawList, testedFormats)) {
                     Log.i(TAG, "Capability is not supported on camera " + id
                             + ". Skip the test.");
                     continue;
@@ -705,6 +710,11 @@
                             previewCaptureSize, rawCaptureSize, rawFormat, previewCaptureCallback,
                             MAX_FRAMES_BURST, rawReaderListener);
 
+                    // Prepare still surface to prevent large allocations slow down capture
+                    mSession.prepare(mReaderSurface);
+                    mSessionListener.waitForSurfacePrepared(
+                            mSession, mReaderSurface, PREPARE_TIMEOUT_MS);
+
                     // execute test routine
                     routine.execute(rawBurstBuilder, rawCaptureCallback, rawReaderListener,
                             rawFormat);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index f12bd1c..afd71f5 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -115,12 +115,6 @@
     private static final float EXIF_EXPOSURE_TIME_MIN_ERROR_MARGIN_SEC = 0.002f;
     private static final float EXIF_APERTURE_ERROR_MARGIN = 0.001f;
 
-    // Some exif tags that are not defined by ExifInterface but supported.
-    private static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
-    private static final String TAG_SUBSEC_TIME = "SubSecTime";
-    private static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
-    private static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
-
     private static final Location sTestLocation0 = new Location(LocationManager.GPS_PROVIDER);
     private static final Location sTestLocation1 = new Location(LocationManager.GPS_PROVIDER);
     private static final Location sTestLocation2 = new Location(LocationManager.NETWORK_PROVIDER);
@@ -1959,7 +1953,7 @@
         }
 
         // TAG_DATETIME_DIGITIZED (a.k.a Create time for digital cameras).
-        String digitizedTime = exif.getAttribute(TAG_DATETIME_DIGITIZED);
+        String digitizedTime = exif.getAttribute(ExifInterface.TAG_DATETIME_DIGITIZED);
         collector.expectNotNull("Exif TAG_DATETIME_DIGITIZED shouldn't be null", digitizedTime);
         if (digitizedTime != null) {
             String expectedDateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
@@ -1977,16 +1971,18 @@
          * this exif tag either doesn't exist or is a non-numerical invalid
          * string. Same rule applies to the rest of sub second tags.
          */
-        int subSecTime = exif.getAttributeInt(TAG_SUBSEC_TIME, /*defaultValue*/-1);
+        int subSecTime = exif.getAttributeInt(ExifInterface.TAG_SUBSEC_TIME, /*defaultValue*/-1);
         collector.expectTrue("Exif TAG_SUBSEC_TIME value is null or invalid!", subSecTime > 0);
 
         // TAG_SUBSEC_TIME_ORIG
-        int subSecTimeOrig = exif.getAttributeInt(TAG_SUBSEC_TIME_ORIG, /*defaultValue*/-1);
+        int subSecTimeOrig = exif.getAttributeInt(ExifInterface.TAG_SUBSEC_TIME_ORIG,
+                /*defaultValue*/-1);
         collector.expectTrue("Exif TAG_SUBSEC_TIME_ORIG value is null or invalid!",
                 subSecTimeOrig > 0);
 
         // TAG_SUBSEC_TIME_DIG
-        int subSecTimeDig = exif.getAttributeInt(TAG_SUBSEC_TIME_DIG, /*defaultValue*/-1);
+        int subSecTimeDig = exif.getAttributeInt(ExifInterface.TAG_SUBSEC_TIME_DIG,
+                /*defaultValue*/-1);
         collector.expectTrue(
                 "Exif TAG_SUBSEC_TIME_DIG value is null or invalid!", subSecTimeDig > 0);
     }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
index 31a8d98..8dbceae 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -119,7 +119,9 @@
         double[] stopPreviewTimes = new double[NUM_TEST_LOOPS];
         double[] cameraCloseTimes = new double[NUM_TEST_LOOPS];
         double[] cameraLaunchTimes = new double[NUM_TEST_LOOPS];
+        double[] avgCameraLaunchTimes = new double[mCameraIds.length];
 
+        int counter = 0;
         for (String id : mCameraIds) {
             try {
                 initializeImageReader(id, ImageFormat.YUV_420_888);
@@ -167,6 +169,7 @@
                     }
                 }
 
+                avgCameraLaunchTimes[counter] = Stat.getAverage(cameraLaunchTimes);
                 // Finish the data collection, report the KPIs.
                 mReportLog.printArray("Camera " + id
                         + ": Camera open time", cameraOpenTimes,
@@ -186,14 +189,14 @@
                 mReportLog.printArray("Camera " + id
                         + ": Camera launch time", cameraLaunchTimes,
                         ResultType.LOWER_BETTER, ResultUnit.MS);
-                mReportLog.printSummary("Camera launch average time for Camera " + id,
-                        Stat.getAverage(cameraLaunchTimes),
-                        ResultType.LOWER_BETTER, ResultUnit.MS);
             }
             finally {
                 closeImageReader();
             }
+            counter++;
         }
+        mReportLog.printSummary("Camera launch average time for all cameras ",
+                Stat.getAverage(avgCameraLaunchTimes), ResultType.LOWER_BETTER, ResultUnit.MS);
     }
 
     /**
@@ -213,7 +216,9 @@
         double[] captureTimes = new double[NUM_TEST_LOOPS];
         double[] getPartialTimes = new double[NUM_TEST_LOOPS];
         double[] getResultTimes = new double[NUM_TEST_LOOPS];
+        double[] avgResultTimes = new double[mCameraIds.length];
 
+        int counter = 0;
         for (String id : mCameraIds) {
             try {
                 openDevice(id);
@@ -291,16 +296,18 @@
                         + ": Camera capture result latency", getResultTimes,
                         ResultType.LOWER_BETTER, ResultUnit.MS);
 
-                // Result will not be reported in CTS report if no summary is printed.
-                mReportLog.printSummary("Camera capture result average latency for Camera " + id,
-                        Stat.getAverage(getResultTimes),
-                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                avgResultTimes[counter] = Stat.getAverage(getResultTimes);
             }
             finally {
                 closeImageReader();
                 closeDevice();
             }
+            counter++;
         }
+
+        // Result will not be reported in CTS report if no summary is printed.
+        mReportLog.printSummary("Camera capture result average latency for all cameras ",
+                Stat.getAverage(avgResultTimes), ResultType.LOWER_BETTER, ResultUnit.MS);
     }
 
     /**
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
new file mode 100644
index 0000000..996ddf7
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 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.cts;
+
+import android.content.ComponentName;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService;
+import android.service.media.MediaBrowserService.BrowserRoot;
+import android.test.InstrumentationTestCase;
+
+import java.util.List;
+
+/**
+ * Test {@link android.media.browse.MediaBrowserService}.
+ */
+public class MediaBrowserServiceTest extends InstrumentationTestCase {
+    // The maximum time to wait for an operation.
+    private static final long TIME_OUT_MS = 3000L;
+    private static final long WAIT_TIME_FOR_NO_RESPONSE_MS = 500L;
+    private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
+            "com.android.cts.media", "android.media.cts.StubMediaBrowserService");
+    private final Object mWaitLock = new Object();
+    private final MediaBrowser.ConnectionCallback mConnectionCallback =
+            new MediaBrowser.ConnectionCallback() {
+                @Override
+                public void onConnected() {
+                    synchronized (mWaitLock) {
+                        mMediaBrowserService = StubMediaBrowserService.sInstance;
+                        mWaitLock.notify();
+                    }
+                }
+            };
+
+    private final MediaBrowser.SubscriptionCallback mSubscriptionCallback  =
+            new MediaBrowser.SubscriptionCallback() {
+                @Override
+                public void onChildrenLoaded(String parentId, List<MediaItem> children) {
+                    synchronized (mWaitLock) {
+                        mOnChildrenLoaded = true;
+                        mWaitLock.notify();
+                    }
+                }
+            };
+
+    private MediaBrowser mMediaBrowser;
+    private StubMediaBrowserService mMediaBrowserService;
+    private boolean mOnChildrenLoaded;
+
+    @Override
+    protected void setUp() throws Exception {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser = new MediaBrowser(getInstrumentation().getTargetContext(),
+                        TEST_BROWSER_SERVICE, mConnectionCallback, null);
+            }
+        });
+        synchronized (mWaitLock) {
+            mMediaBrowser.connect();
+            mWaitLock.wait(TIME_OUT_MS);
+        }
+        assertNotNull(mMediaBrowserService);
+    }
+
+    public void testGetSessionToken() {
+        assertEquals(StubMediaBrowserService.sSession.getSessionToken(),
+                mMediaBrowserService.getSessionToken());
+    }
+
+    public void testNotifyChildrenChanged() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mOnChildrenLoaded);
+
+            mOnChildrenLoaded = false;
+            mMediaBrowserService.notifyChildrenChanged(StubMediaBrowserService.MEDIA_ID_ROOT);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mOnChildrenLoaded);
+        }
+    }
+
+    public void testDelayedNotifyChildrenChanged() throws Exception {
+        synchronized (mWaitLock) {
+            mOnChildrenLoaded = false;
+            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED,
+                    mSubscriptionCallback);
+            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertFalse(mOnChildrenLoaded);
+
+            mMediaBrowserService.sendDelayedNotifyChildrenChanged();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mOnChildrenLoaded);
+
+            mOnChildrenLoaded = false;
+            mMediaBrowserService.notifyChildrenChanged(
+                    StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED);
+            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertFalse(mOnChildrenLoaded);
+
+            mMediaBrowserService.sendDelayedNotifyChildrenChanged();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mOnChildrenLoaded);
+        }
+    }
+
+    public void testBrowserRoot() {
+        final String id = "test-id";
+        final String key = "test-key";
+        final String val = "test-val";
+        final Bundle extras = new Bundle();
+        extras.putString(key, val);
+
+        MediaBrowserService.BrowserRoot browserRoot = new BrowserRoot(id, extras);
+        assertEquals(id, browserRoot.getRootId());
+        assertEquals(val, browserRoot.getExtras().getString(key));
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
index 976e5a6..51d43d9 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -27,7 +27,7 @@
  */
 public class MediaBrowserTest extends InstrumentationTestCase {
     // The maximum time to wait for an operation.
-    private static final long TIME_OUT_MS = 1000L;
+    private static final long TIME_OUT_MS = 3000L;
     private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
             "com.android.cts.media", "android.media.cts.StubMediaBrowserService");
     private static final ComponentName TEST_INVALID_BROWSER_SERVICE = new ComponentName(
diff --git a/tests/tests/media/src/android/media/cts/MediaControllerTest.java b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
index b934516..a153c4d 100644
--- a/tests/tests/media/src/android/media/cts/MediaControllerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
@@ -15,13 +15,16 @@
  */
 package android.media.cts;
 
+import android.media.AudioManager;
 import android.media.Rating;
+import android.media.VolumeProvider;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState.CustomAction;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.ResultReceiver;
 import android.test.AndroidTestCase;
 
 /**
@@ -29,129 +32,233 @@
  */
 public class MediaControllerTest extends AndroidTestCase {
     // The maximum time to wait for an operation.
-    private static final long TIME_OUT_MS = 5000L;
+    private static final long TIME_OUT_MS = 3000L;
     private static final String SESSION_TAG = "test-session";
     private static final String EXTRAS_KEY = "test-key";
     private static final String EXTRAS_VALUE = "test-val";
 
+    private final Object mWaitLock = new Object();
     private Handler mHandler = new Handler(Looper.getMainLooper());
+    private MediaSession mSession;
+    private MediaSessionCallback mCallback = new MediaSessionCallback();
+    private MediaController mController;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mSession = new MediaSession(getContext(), SESSION_TAG);
+        mSession.setCallback(mCallback, mHandler);
+        mController = mSession.getController();
+    }
+
+    public void testSendCommand() throws Exception {
+        synchronized (mWaitLock) {
+            mCallback.reset();
+            final String command = "test-command";
+            final Bundle extras = new Bundle();
+            extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+            mController.sendCommand(command, extras, new ResultReceiver(null));
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnCommandCalled);
+            assertNotNull(mCallback.mCommandCallback);
+            assertEquals(command, mCallback.mCommand);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+        }
+    }
+
+    public void testVolumeControl() throws Exception {
+        VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_ABSOLUTE, 11, 5) {
+            @Override
+            public void onSetVolumeTo(int volume) {
+                synchronized (mWaitLock) {
+                    setCurrentVolume(volume);
+                    mWaitLock.notify();
+                }
+            }
+
+            @Override
+            public void onAdjustVolume(int direction) {
+                synchronized (mWaitLock) {
+                    switch (direction) {
+                        case AudioManager.ADJUST_LOWER:
+                            setCurrentVolume(getCurrentVolume() - 1);
+                            break;
+                        case AudioManager.ADJUST_RAISE:
+                            setCurrentVolume(getCurrentVolume() + 1);
+                            break;
+                    }
+                    mWaitLock.notify();
+                }
+            }
+        };
+        mSession.setPlaybackToRemote(vp);
+
+        synchronized (mWaitLock) {
+            // test setVolumeTo
+            mController.setVolumeTo(7, 0);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(7, vp.getCurrentVolume());
+
+            // test adjustVolume
+            mController.adjustVolume(AudioManager.ADJUST_LOWER, 0);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(6, vp.getCurrentVolume());
+
+            mController.adjustVolume(AudioManager.ADJUST_RAISE, 0);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(7, vp.getCurrentVolume());
+        }
+    }
 
     public void testTransportControlsAndMediaSessionCallback() throws Exception {
-        Object waitLock = new Object();
-        MediaSession session = new MediaSession(getContext(), SESSION_TAG);
-        MediaSessionCallback callback = new MediaSessionCallback(waitLock);
-        session.setCallback(callback, mHandler);
-
-        MediaController.TransportControls controls =
-                session.getController().getTransportControls();
-        synchronized (waitLock) {
+        MediaController.TransportControls controls = mController.getTransportControls();
+        synchronized (mWaitLock) {
+            mCallback.reset();
             controls.play();
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnPlayCalled);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPlayCalled);
 
+            mCallback.reset();
             controls.pause();
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnPauseCalled);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPauseCalled);
 
+            mCallback.reset();
             controls.stop();
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnStopCalled);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnStopCalled);
 
+            mCallback.reset();
             controls.fastForward();
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnFastForwardCalled);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnFastForwardCalled);
 
+            mCallback.reset();
             controls.rewind();
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnRewindCalled);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnRewindCalled);
 
+            mCallback.reset();
             controls.skipToPrevious();
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnSkipToPreviousCalled);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSkipToPreviousCalled);
 
+            mCallback.reset();
             controls.skipToNext();
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnSkipToNextCalled);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSkipToNextCalled);
 
+            mCallback.reset();
             final long seekPosition = 1000;
             controls.seekTo(seekPosition);
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnSeekToCalled);
-            assertEquals(seekPosition, callback.mSeekPosition);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSeekToCalled);
+            assertEquals(seekPosition, mCallback.mSeekPosition);
 
+            mCallback.reset();
             final Rating rating = Rating.newStarRating(Rating.RATING_5_STARS, 3f);
             controls.setRating(rating);
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnSetRatingCalled);
-            assertEquals(rating.getRatingStyle(), callback.mRating.getRatingStyle());
-            assertEquals(rating.getStarRating(), callback.mRating.getStarRating());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSetRatingCalled);
+            assertEquals(rating.getRatingStyle(), mCallback.mRating.getRatingStyle());
+            assertEquals(rating.getStarRating(), mCallback.mRating.getStarRating());
 
+            mCallback.reset();
             final String mediaId = "test-media-id";
             final Bundle extras = new Bundle();
             extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
             controls.playFromMediaId(mediaId, extras);
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnPlayFromMediaIdCalled);
-            assertEquals(mediaId, callback.mMediaId);
-            assertEquals(EXTRAS_VALUE, callback.mExtras.getString(EXTRAS_KEY));
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPlayFromMediaIdCalled);
+            assertEquals(mediaId, mCallback.mMediaId);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
 
+            mCallback.reset();
             final String query = "test-query";
             controls.playFromSearch(query, extras);
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnPlayFromSearchCalled);
-            assertEquals(query, callback.mQuery);
-            assertEquals(EXTRAS_VALUE, callback.mExtras.getString(EXTRAS_KEY));
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPlayFromSearchCalled);
+            assertEquals(query, mCallback.mQuery);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
 
+            mCallback.reset();
             final String action = "test-action";
             controls.sendCustomAction(action, extras);
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnCustomActionCalled);
-            assertEquals(action, callback.mAction);
-            assertEquals(EXTRAS_VALUE, callback.mExtras.getString(EXTRAS_KEY));
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnCustomActionCalled);
+            assertEquals(action, mCallback.mAction);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
 
-            callback.mOnCustomActionCalled = false;
+            mCallback.reset();
+            mCallback.mOnCustomActionCalled = false;
             final CustomAction customAction =
                     new CustomAction.Builder(action, action, -1).setExtras(extras).build();
             controls.sendCustomAction(customAction, extras);
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnCustomActionCalled);
-            assertEquals(action, callback.mAction);
-            assertEquals(EXTRAS_VALUE, callback.mExtras.getString(EXTRAS_KEY));
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnCustomActionCalled);
+            assertEquals(action, mCallback.mAction);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
 
+            mCallback.reset();
             final long queueItemId = 1000;
             controls.skipToQueueItem(queueItemId);
-            waitLock.wait(TIME_OUT_MS);
-            assertTrue(callback.mOnSkipToQueueItemCalled);
-            assertEquals(queueItemId, callback.mQueueItemId);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnSkipToQueueItemCalled);
+            assertEquals(queueItemId, mCallback.mQueueItemId);
         }
     }
 
     private class MediaSessionCallback extends MediaSession.Callback {
-        private Object mWaitLock;
-        private long mSeekPosition;
-        private long mQueueItemId;
-        private Rating mRating;
-        private String mMediaId;
-        private String mQuery;
-        private String mAction;
-        private Bundle mExtras;
+        private volatile long mSeekPosition;
+        private volatile long mQueueItemId;
+        private volatile Rating mRating;
+        private volatile String mMediaId;
+        private volatile String mQuery;
+        private volatile String mAction;
+        private volatile String mCommand;
+        private volatile Bundle mExtras;
+        private volatile ResultReceiver mCommandCallback;
 
-        private boolean mOnPlayCalled;
-        private boolean mOnPauseCalled;
-        private boolean mOnStopCalled;
-        private boolean mOnFastForwardCalled;
-        private boolean mOnRewindCalled;
-        private boolean mOnSkipToPreviousCalled;
-        private boolean mOnSkipToNextCalled;
-        private boolean mOnSeekToCalled;
-        private boolean mOnSetRatingCalled;
-        private boolean mOnPlayFromMediaIdCalled;
-        private boolean mOnPlayFromSearchCalled;
-        private boolean mOnCustomActionCalled;
-        private boolean mOnSkipToQueueItemCalled;
+        private volatile boolean mOnPlayCalled;
+        private volatile boolean mOnPauseCalled;
+        private volatile boolean mOnStopCalled;
+        private volatile boolean mOnFastForwardCalled;
+        private volatile boolean mOnRewindCalled;
+        private volatile boolean mOnSkipToPreviousCalled;
+        private volatile boolean mOnSkipToNextCalled;
+        private volatile boolean mOnSeekToCalled;
+        private volatile boolean mOnSkipToQueueItemCalled;
+        private volatile boolean mOnSetRatingCalled;
+        private volatile boolean mOnPlayFromMediaIdCalled;
+        private volatile boolean mOnPlayFromSearchCalled;
+        private volatile boolean mOnCustomActionCalled;
+        private volatile boolean mOnCommandCalled;
 
-        public MediaSessionCallback(Object lock) {
-            mWaitLock = lock;
+        public void reset() {
+            mSeekPosition = -1;
+            mQueueItemId = -1;
+            mRating = null;
+            mMediaId = null;
+            mQuery = null;
+            mAction = null;
+            mExtras = null;
+            mCommand = null;
+            mCommandCallback = null;
+
+            mOnPlayCalled = false;
+            mOnPauseCalled = false;
+            mOnStopCalled = false;
+            mOnFastForwardCalled = false;
+            mOnRewindCalled = false;
+            mOnSkipToPreviousCalled = false;
+            mOnSkipToNextCalled = false;
+            mOnSkipToQueueItemCalled = false;
+            mOnSeekToCalled = false;
+            mOnSetRatingCalled = false;
+            mOnPlayFromMediaIdCalled = false;
+            mOnPlayFromSearchCalled = false;
+            mOnCustomActionCalled = false;
+            mOnCommandCalled = false;
         }
 
         @Override
@@ -266,5 +373,16 @@
                 mWaitLock.notify();
             }
         }
+
+        @Override
+        public void onCommand(String command, Bundle extras, ResultReceiver cb) {
+            synchronized (mWaitLock) {
+                mOnCommandCalled = true;
+                mCommand = command;
+                mExtras = extras;
+                mCommandCallback = cb;
+                mWaitLock.notify();
+            }
+        }
     }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionTest.java b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
index 7999092..a2dbe7d 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
@@ -175,6 +175,10 @@
         PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0);
         session.setSessionActivity(pi);
         assertEquals(pi, controller.getSessionActivity());
+
+        // test setActivity
+        session.setActive(true);
+        assertTrue(session.isActive());
     }
 
     public void testSendSessionEvent() throws Exception {
diff --git a/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java b/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
index bfd6db7..35e88f0 100644
--- a/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
+++ b/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
@@ -23,8 +23,11 @@
 import android.service.media.MediaBrowserService;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
+import junit.framework.Assert;
+
 /**
  * Stub implementation of (@link android.service.media.MediaBrowserService}.
  */
@@ -32,18 +35,24 @@
     static final String MEDIA_ID_ROOT = "test_media_id_root";
     static final String EXTRAS_KEY = "test_extras_key";
     static final String EXTRAS_VALUE = "test_extras_value";
+    static final String MEDIA_ID_CHILDREN_DELAYED = "test_media_id_children_delayed";
     static final String[] MEDIA_ID_CHILDREN = new String[] {
         "test_media_id_children_0", "test_media_id_children_1",
-        "test_media_id_children_2", "test_media_id_children_3"
+        "test_media_id_children_2", "test_media_id_children_3",
+        MEDIA_ID_CHILDREN_DELAYED
     };
 
+    static StubMediaBrowserService sInstance;
+
     /* package private */ static MediaSession sSession;
     private Bundle mExtras;
+    private Result<List<MediaItem>> mPendingResult;
 
     @Override
     public void onCreate() {
         super.onCreate();
-        sSession = new MediaSession(this, "MediaBrowserStubService");
+        sInstance = this;
+        sSession = new MediaSession(this, "StubMediaBrowserService");
         setSessionToken(sSession.getSessionToken());
     }
 
@@ -62,7 +71,18 @@
                 mediaItems.add(new MediaItem(new MediaDescription.Builder()
                         .setMediaId(id).build(), MediaItem.FLAG_BROWSABLE));
             }
+            result.sendResult(mediaItems);
+        } else if (MEDIA_ID_CHILDREN_DELAYED.equals(parentMediaId)) {
+            Assert.assertNull(mPendingResult);
+            mPendingResult = result;
+            result.detach();
         }
-        result.sendResult(mediaItems);
+    }
+
+    public void sendDelayedNotifyChildrenChanged() {
+        if (mPendingResult != null) {
+            mPendingResult.sendResult(Collections.<MediaItem>emptyList());
+            mPendingResult = null;
+        }
     }
 }
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index 5122f24..0c311e0 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -164,7 +164,6 @@
       "9D:70:BB:01:A5:A4:A0:18:11:2E:F7:1C:01:B9:32:C5:34:E7:88:A8",
       "96:C9:1B:0B:95:B4:10:98:42:FA:D0:D8:22:79:FE:60:FA:B9:16:83",
       "D8:A6:33:2C:E0:03:6F:B1:85:F6:63:4F:7D:6A:06:65:26:32:28:27",
-      "9F:AD:91:A6:CE:6A:C6:C5:00:47:C4:4E:C9:D4:A5:0D:92:D8:49:79",
       "CC:AB:0E:A0:4C:23:01:D6:69:7B:DD:37:9F:CD:12:EB:24:E3:94:9D",
       "48:12:BD:92:3C:A8:C4:39:06:E7:30:6D:27:96:E6:A4:CF:22:2E:7D",
       "F9:B5:B6:32:45:5F:9C:BE:EC:57:5F:80:DC:E9:6E:2C:C7:B2:78:B7",
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
new file mode 100644
index 0000000..a569058
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2015 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.telecom.cts;
+
+import static android.telecom.cts.TestUtils.*;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telecom.Call;
+import android.telecom.CallAudioState;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.InCallService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telecom.cts.MockConnectionService.ConnectionServiceCallbacks;
+import android.telecom.cts.MockInCallService.InCallServiceCallbacks;
+import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Base class for Telecom CTS tests that require a {@link MockConnectionService} and
+ * {@link MockInCallService} to verify Telecom functionality.
+ */
+public class BaseTelecomTestWithMockServices extends InstrumentationTestCase {
+    public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
+            new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID);
+
+    public static final PhoneAccount TEST_PHONE_ACCOUNT = PhoneAccount.builder(
+            TEST_PHONE_ACCOUNT_HANDLE, LABEL)
+            .setAddress(Uri.parse("tel:555-TEST"))
+            .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
+            .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+            .setHighlightColor(Color.RED)
+            .setShortDescription(LABEL)
+            .setSupportedUriSchemes(Arrays.asList("tel"))
+            .build();
+
+    private static int sCounter = 0;
+
+    Context mContext;
+    TelecomManager mTelecomManager;
+    InCallServiceCallbacks mInCallCallbacks;
+    ConnectionServiceCallbacks mConnectionCallbacks;
+    String mPreviousDefaultDialer = null;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getContext();
+        mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+
+        if (shouldTestTelecom(mContext)) {
+            mTelecomManager.registerPhoneAccount(TEST_PHONE_ACCOUNT);
+            TestUtils.enablePhoneAccount(getInstrumentation(), TEST_PHONE_ACCOUNT_HANDLE);
+            mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
+            TestUtils.setDefaultDialer(getInstrumentation(), PACKAGE);
+            setupCallbacks();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (shouldTestTelecom(mContext)) {
+            if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
+                TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
+            }
+            mTelecomManager.unregisterPhoneAccount(TEST_PHONE_ACCOUNT_HANDLE);
+        }
+        super.tearDown();
+    }
+
+    private void sleep(long ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    private void setupCallbacks() {
+        mInCallCallbacks = new InCallServiceCallbacks() {
+            @Override
+            public void onCallAdded(Call call, int numCalls) {
+                this.lock.release();
+            }
+        };
+
+        MockInCallService.setCallbacks(mInCallCallbacks);
+
+        mConnectionCallbacks = new ConnectionServiceCallbacks() {
+            @Override
+            public void onCreateOutgoingConnection(MockConnection connection,
+                    ConnectionRequest request) {
+                this.lock.release();
+            }
+
+            @Override
+            public void onCreateIncomingConnection(MockConnection connection,
+                    ConnectionRequest request) {
+                this.lock.release();
+            }
+        };
+
+        MockConnectionService.setCallbacks(mConnectionCallbacks);
+    }
+
+    /**
+     * Puts Telecom in a state where there is an incoming call provided by the
+     * {@link MockConnectionService} which can be tested.
+     */
+    void addAndVerifyNewIncomingCall(Uri incomingHandle, Bundle extras) {
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, incomingHandle);
+        mTelecomManager.addNewIncomingCall(TEST_PHONE_ACCOUNT_HANDLE, extras);
+
+        try {
+            if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+                fail("No call added to InCallService.");
+            }
+        } catch (InterruptedException e) {
+            Log.i(TAG, "Test interrupted!");
+        }
+
+        assertEquals("InCallService should contain 1 call after adding a call.", 1,
+                mInCallCallbacks.getService().getCallCount());
+    }
+
+    /**
+     *  Puts Telecom in a state where there is an active call provided by the
+     *  {@link MockConnectionService} which can be tested.
+     */
+    void placeAndVerifyCall() {
+        placeAndVerifyCall(null);
+    }
+
+    /**
+     *  Puts Telecom in a state where there is an active call provided by the
+     *  {@link MockConnectionService} which can be tested.
+     */
+    void placeAndVerifyCall(Bundle extras) {
+        placeNewCallWithPhoneAccount(extras);
+
+        try {
+            if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+                fail("No call added to InCallService.");
+            }
+        } catch (InterruptedException e) {
+            Log.i(TAG, "Test interrupted!");
+        }
+
+        assertEquals("InCallService should contain 1 call after adding a call.", 1,
+                mInCallCallbacks.getService().getCallCount());
+    }
+
+    void verifyConnectionForOutgoingCall() {
+        try {
+            if (!mConnectionCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+                fail("No outgoing call connection requested by Telecom");
+            }
+        } catch (InterruptedException e) {
+            Log.i(TAG, "Test interrupted!");
+        }
+
+        assertNotNull("Telecom should bind to and create ConnectionService",
+                mConnectionCallbacks.getService());
+        assertNotNull("Telecom should create outgoing connection for outgoing call",
+                mConnectionCallbacks.outgoingConnection);
+        assertNull("Telecom should not create incoming connection for outgoing call",
+                mConnectionCallbacks.incomingConnection);
+
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        connection.setDialing();
+        connection.setActive();
+
+        assertEquals(Connection.STATE_ACTIVE, connection.getState());
+    }
+
+    void verifyConnectionForIncomingCall() {
+        try {
+            if (!mConnectionCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+                fail("No incoming call connection requested by Telecom");
+            }
+        } catch (InterruptedException e) {
+            Log.i(TAG, "Test interrupted!");
+        }
+
+        assertNotNull("Telecom should bind to and create ConnectionService",
+                mConnectionCallbacks.getService());
+        assertNull("Telecom should not create outgoing connection for outgoing call",
+                mConnectionCallbacks.outgoingConnection);
+        assertNotNull("Telecom should create incoming connection for outgoing call",
+                mConnectionCallbacks.incomingConnection);
+
+        final MockConnection connection = mConnectionCallbacks.incomingConnection;
+        connection.setRinging();
+        assertEquals(Connection.STATE_RINGING, connection.getState());
+    }
+
+    /**
+     * Disconnect the created test call, verify that Telecom has cleared all calls and has
+     * unbound from the {@link ConnectionService}.
+     */
+    void cleanupAndVerifyUnbind() {
+        if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
+            mInCallCallbacks.prepareForUnbind();
+
+            mInCallCallbacks.getService().disconnectLastCall();
+            assertNumCalls(mInCallCallbacks.getService(), 0);
+
+            try {
+                if (!mInCallCallbacks.unbindLock.tryAcquire(3, TimeUnit.SECONDS)) {
+                    fail("Telecom did not unbind from InCallService after all calls removed.");
+                }
+            } catch (InterruptedException e) {
+                Log.i(TAG, "Test interrupted!");
+            }
+        }
+    }
+
+    /**
+     * Place a new outgoing call via the {@link MockConnectionService}
+     */
+    private void placeNewCallWithPhoneAccount(Bundle extras) {
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEST_PHONE_ACCOUNT_HANDLE);
+        mTelecomManager.placeCall(getTestNumber(), extras);
+    }
+
+    /**
+     * Create a new number each time for a new test. Telecom has special logic to reuse certain
+     * calls if multiple calls to the same number are placed within a short period of time which
+     * can cause certain tests to fail.
+     */
+    Uri getTestNumber() {
+        return Uri.fromParts("tel", String.valueOf(sCounter++), null);
+    }
+
+    void assertNumCalls(final MockInCallService inCallService, final int numCalls) {
+        waitUntilConditionIsTrueOrTimeout(new Condition() {
+            @Override
+            public Object expected() {
+                return numCalls;
+            }
+            @Override
+            public Object actual() {
+                return inCallService.getCallCount();
+            }
+        },
+        WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+        "InCallService should contain " + numCalls + " calls."
+    );
+    }
+
+    void assertMuteState(final InCallService incallService, final boolean isMuted) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return isMuted;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        final CallAudioState state = incallService.getCallAudioState();
+                        return state == null ? null : state.isMuted();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Phone's mute state should be: " + isMuted
+        );
+    }
+
+    void assertMuteState(final MockConnection connection, final boolean isMuted) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return isMuted;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        final CallAudioState state = connection.getCallAudioState();
+                        return state == null ? null : state.isMuted();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Connection's mute state should be: " + isMuted
+        );
+    }
+
+    void assertAudioRoute(final InCallService incallService, final int route) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return route;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        final CallAudioState state = incallService.getCallAudioState();
+                        return state == null ? null : state.getRoute();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Phone's audio route should be: " + route
+        );
+    }
+
+    void assertAudioRoute(final MockConnection connection, final int route) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return route;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        final CallAudioState state = connection.getCallAudioState();
+                        return state == null ? null : state.getRoute();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Connection's audio route should be: " + route
+        );
+    }
+
+    void assertConnectionState(final Connection connection, final int state) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return state;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return connection.getState();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Connection should be in state " + state
+        );
+    }
+
+    void assertCallState(final Call call, final int state) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return state;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return call.getState();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Call should be in state " + state
+        );
+    }
+
+    void assertDtmfString(final MockConnection connection, final String dtmfString) {
+        waitUntilConditionIsTrueOrTimeout(new Condition() {
+                @Override
+                public Object expected() {
+                    return dtmfString;
+                }
+
+                @Override
+                public Object actual() {
+                    return connection.getDtmfString();
+                }
+            },
+            WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+            "DTMF string should be equivalent to entered DTMF characters: " + dtmfString
+        );
+    }
+
+    void waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout,
+            String description) {
+        final long start = System.currentTimeMillis();
+        while (!condition.expected().equals(condition.actual())
+                && System.currentTimeMillis() - start < timeout) {
+            sleep(50);
+        }
+        assertEquals(description, condition.expected(), condition.actual());
+    }
+
+    private interface Condition {
+        Object expected();
+        Object actual();
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index 1a9d8d7..c4ec5c4 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -18,85 +18,30 @@
 
 import static android.telecom.cts.TestUtils.*;
 
-import android.telecom.cts.MockConnectionService.ConnectionServiceCallbacks;
-import android.telecom.cts.MockInCallService.InCallServiceCallbacks;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Color;
-import android.net.Uri;
 import android.telecom.CallAudioState;
 import android.telecom.Call;
 import android.telecom.Connection;
-import android.telecom.ConnectionRequest;
 import android.telecom.ConnectionService;
 import android.telecom.InCallService;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.test.InstrumentationTestCase;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.util.Arrays;
-import java.util.concurrent.TimeUnit;
 
 /**
- * Extended suite of tests that use {@MockConnectionService} and {@MockInCallService} to verify
- * the functionality of the Telecom service. Requires that the version of GmsCore installed on the
- * device has the REGISTER_CALL_PROVIDER permission.
+ * Extended suite of tests that use {@link MockConnectionService} and {@link MockInCallService} to
+ * verify the functionality of the Telecom service.
  */
-public class ExtendedInCallServiceTest extends InstrumentationTestCase {
-    public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
-            new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID);
-
-    public static final PhoneAccount TEST_PHONE_ACCOUNT = PhoneAccount.builder(
-            TEST_PHONE_ACCOUNT_HANDLE, LABEL)
-            .setAddress(Uri.parse("tel:555-TEST"))
-            .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
-            .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
-            .setHighlightColor(Color.RED)
-            .setShortDescription(LABEL)
-            .setSupportedUriSchemes(Arrays.asList("tel"))
-            .build();
-
-    private Context mContext;
-    private TelecomManager mTelecomManager;
-    private InCallServiceCallbacks mInCallCallbacks;
-    private ConnectionServiceCallbacks mConnectionCallbacks;
-    private String mPreviousDefaultDialer = null;
-
-    private static int sCounter = 0;
-
+public class ExtendedInCallServiceTest extends BaseTelecomTestWithMockServices {
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mContext = getInstrumentation().getContext();
-        mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
-
         if (shouldTestTelecom(mContext)) {
-            mTelecomManager.registerPhoneAccount(TEST_PHONE_ACCOUNT);
-            TestUtils.enablePhoneAccount(getInstrumentation(), TEST_PHONE_ACCOUNT_HANDLE);
-            mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
-            TestUtils.setDefaultDialer(getInstrumentation(), PACKAGE);
-            setupCallbacks();
             placeAndVerifyCall();
-            verifyConnectionService();
+            verifyConnectionForOutgoingCall();
         }
     }
 
     @Override
     protected void tearDown() throws Exception {
         if (shouldTestTelecom(mContext)) {
-            if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
-                mInCallCallbacks.getService().disconnectLastCall();
-                assertNumCalls(mInCallCallbacks.getService(), 0);
-            }
-            if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
-                TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
-            }
-            mTelecomManager.unregisterPhoneAccount(TEST_PHONE_ACCOUNT_HANDLE);
+            cleanupAndVerifyUnbind();
         }
         super.tearDown();
     }
@@ -208,232 +153,4 @@
         assertCallState(call, Call.STATE_ACTIVE);
         assertEquals(Connection.STATE_ACTIVE, connection.getState());
     }
-
-    private void sleep(long ms) {
-        try {
-            Thread.sleep(ms);
-        } catch (InterruptedException e) {
-        }
-    }
-
-    private void setupCallbacks() {
-        mInCallCallbacks = new InCallServiceCallbacks() {
-            @Override
-            public void onCallAdded(Call call, int numCalls) {
-                this.lock.release();
-            }
-        };
-
-        MockInCallService.setCallbacks(mInCallCallbacks);
-
-        mConnectionCallbacks = new ConnectionServiceCallbacks() {
-            @Override
-            public void onCreateOutgoingConnection(MockConnection connection,
-                    ConnectionRequest request) {
-                this.lock.release();
-            }
-        };
-
-        MockConnectionService.setCallbacks(mConnectionCallbacks);
-    }
-
-    /**
-     *  Puts Telecom in a state where there is an active call provided by the
-     *  {@link MockConnectionService} which can be tested.
-     */
-    private void placeAndVerifyCall() {
-        placeNewCallWithPhoneAccount();
-
-        try {
-            if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
-                fail("No call added to InCallService.");
-            }
-        } catch (InterruptedException e) {
-            Log.i(TAG, "Test interrupted!");
-        }
-
-        assertEquals("InCallService should contain 1 call after adding a call.", 1,
-                mInCallCallbacks.getService().getCallCount());
-        assertTrue("TelecomManager should be in a call", mTelecomManager.isInCall());
-    }
-
-    private void verifyConnectionService() {
-        try {
-            if (!mConnectionCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
-                fail("No outgoing call connection requested by Telecom");
-            }
-        } catch (InterruptedException e) {
-            Log.i(TAG, "Test interrupted!");
-        }
-
-        assertNotNull("Telecom should bind to and create ConnectionService",
-                mConnectionCallbacks.getService());
-        assertNotNull("Telecom should create outgoing connection for outgoing call",
-                mConnectionCallbacks.outgoingConnection);
-        assertNull("Telecom should not create incoming connection for outgoing call",
-                mConnectionCallbacks.incomingConnection);
-
-        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
-        connection.setDialing();
-        connection.setActive();
-
-        assertEquals(Connection.STATE_ACTIVE, connection.getState());
-    }
-
-    /**
-     * Place a new outgoing call via the {@link MockConnectionService}
-     */
-    private void placeNewCallWithPhoneAccount() {
-        final Intent intent = new Intent(Intent.ACTION_CALL, getTestNumber());
-        intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEST_PHONE_ACCOUNT_HANDLE);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(intent);
-    }
-
-    /**
-     * Create a new number each time for a new test. Telecom has special logic to reuse certain
-     * calls if multiple calls to the same number are placed within a short period of time which
-     * can cause certain tests to fail.
-     */
-    private Uri getTestNumber() {
-        return Uri.fromParts("tel", String.valueOf(sCounter++), null);
-    }
-
-    private void assertNumCalls(final MockInCallService inCallService, final int numCalls) {
-        waitUntilConditionIsTrueOrTimeout(new Condition() {
-            @Override
-            public Object expected() {
-                return numCalls;
-            }
-            @Override
-            public Object actual() {
-                return inCallService.getCallCount();
-            }
-        },
-        WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
-        "InCallService should contain " + numCalls + " calls."
-    );
-    }
-
-    private void assertMuteState(final InCallService incallService, final boolean isMuted) {
-        waitUntilConditionIsTrueOrTimeout(
-                new Condition() {
-                    @Override
-                    public Object expected() {
-                        return isMuted;
-                    }
-
-                    @Override
-                    public Object actual() {
-                        return incallService.getCallAudioState().isMuted();
-                    }
-                },
-                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
-                "Phone's mute state should be: " + isMuted
-        );
-    }
-
-    private void assertMuteState(final MockConnection connection, final boolean isMuted) {
-        waitUntilConditionIsTrueOrTimeout(
-                new Condition() {
-                    @Override
-                    public Object expected() {
-                        return isMuted;
-                    }
-
-                    @Override
-                    public Object actual() {
-                        return connection.getCallAudioState().isMuted();
-                    }
-                },
-                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
-                "Connection's mute state should be: " + isMuted
-        );
-    }
-
-    private void assertAudioRoute(final InCallService incallService, final int route) {
-        waitUntilConditionIsTrueOrTimeout(
-                new Condition() {
-                    @Override
-                    public Object expected() {
-                        return route;
-                    }
-
-                    @Override
-                    public Object actual() {
-                        return incallService.getCallAudioState().getRoute();
-                    }
-                },
-                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
-                "Phone's audio route should be: " + route
-        );
-    }
-
-    private void assertAudioRoute(final MockConnection connection, final int route) {
-        waitUntilConditionIsTrueOrTimeout(
-                new Condition() {
-                    @Override
-                    public Object expected() {
-                        return route;
-                    }
-
-                    @Override
-                    public Object actual() {
-                        return connection.getCallAudioState().getRoute();
-                    }
-                },
-                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
-                "Connection's audio route should be: " + route
-        );
-    }
-
-    private void assertCallState(final Call call, final int state) {
-        waitUntilConditionIsTrueOrTimeout(
-                new Condition() {
-                    @Override
-                    public Object expected() {
-                        return state;
-                    }
-
-                    @Override
-                    public Object actual() {
-                        return call.getState();
-                    }
-                },
-                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
-                "Call should be in state " + state
-        );
-    }
-
-    private void assertDtmfString(final MockConnection connection, final String dtmfString) {
-        waitUntilConditionIsTrueOrTimeout(new Condition() {
-                @Override
-                public Object expected() {
-                    return dtmfString;
-                }
-
-                @Override
-                public Object actual() {
-                    return connection.getDtmfString();
-                }
-            },
-            WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
-            "DTMF string should be equivalent to entered DTMF characters: " + dtmfString
-        );
-    }
-
-    private void waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout,
-            String description) {
-        final long start = System.currentTimeMillis();
-        while (!condition.expected().equals(condition.actual())
-                && System.currentTimeMillis() - start < timeout) {
-            sleep(50);
-        }
-        assertEquals(description, condition.expected(), condition.actual());
-    }
-
-    private interface Condition {
-        Object expected();
-        Object actual();
-    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
index cecc603..3c48ddd 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
@@ -16,6 +16,7 @@
 
 package android.telecom.cts;
 
+import android.content.Intent;
 import android.telecom.Call;
 import android.telecom.InCallService;
 
@@ -31,6 +32,7 @@
     public static abstract class InCallServiceCallbacks {
         private MockInCallService mService;
         public Semaphore lock = new Semaphore(0);
+        public Semaphore unbindLock = null;
 
         public void onCallAdded(Call call, int numCalls) {};
         public void onCallRemoved(Call call, int numCalls) {};
@@ -43,6 +45,10 @@
         final public void setService(MockInCallService service) {
             mService = service;
         }
+
+        final public void prepareForUnbind() {
+            unbindLock = new Semaphore(0);
+        }
     }
 
     private Call.Callback mCallCallback = new Call.Callback() {
@@ -63,6 +69,14 @@
     }
 
     @Override
+    public boolean onUnbind(Intent intent) {
+        if (getCallbacks() != null && getCallbacks().unbindLock != null) {
+            getCallbacks().unbindLock.release();
+        }
+        return super.onUnbind(intent);
+    }
+
+    @Override
     public void onCallAdded(Call call) {
         if (!mCalls.contains(call)) {
             mCalls.add(call);
diff --git a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
new file mode 100644
index 0000000..5895267
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 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.telecom.cts;
+
+import static android.telecom.cts.TestUtils.shouldTestTelecom;
+
+import android.os.Bundle;
+import android.telecom.CallAudioState;
+import android.telecom.TelecomManager;
+
+/**
+ * Verifies the behavior of Telecom during various outgoing call flows.
+ */
+public class OutgoingCallTest extends BaseTelecomTestWithMockServices {
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (shouldTestTelecom(mContext)) {
+            cleanupAndVerifyUnbind();
+        }
+        super.tearDown();
+    }
+
+    // TODO: Need to send some commands to the UserManager via adb to do setup
+    public void testDisallowOutgoingCallsForSecondaryUser() {
+
+    }
+
+    // TODO: Need to figure out a way to mock emergency calls without adb root
+    public void testOutgoingCallBroadcast_isSentForAllCalls() {
+
+    }
+
+    /**
+     * Verifies that providing the EXTRA_START_CALL_WITH_SPEAKERPHONE extra starts the call with
+     * speakerphone automatically enabled.
+     *
+     * @see {@link TelecomManager#EXTRA_START_CALL_WITH_SPEAKERPHONE}
+     */
+    public void testStartCallWithSpeakerphoneTrue_SpeakerphoneOnInCall() {
+        final Bundle extras = new Bundle();
+        extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);
+        placeAndVerifyCall(extras);
+        verifyConnectionForOutgoingCall();
+        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_SPEAKER);
+    }
+
+    public void testStartCallWithSpeakerphoneFalse_SpeakerphoneOffInCall() {
+        final Bundle extras = new Bundle();
+        extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
+        placeAndVerifyCall(extras);
+        verifyConnectionForOutgoingCall();
+        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+    }
+
+    public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
new file mode 100644
index 0000000..4e154cf
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 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.telecom.cts;
+
+import android.telecom.Call;
+import android.telecom.CallAudioState;
+import android.telecom.Connection;
+
+/**
+ * Verifies Telecom behavior with regards to interactions with a wired headset. These tests
+ * validate behavior that occurs as a result of short pressing or long pressing a wired headset's
+ * media button.
+ */
+public class WiredHeadsetTest extends BaseTelecomTestWithMockServices {
+    @Override
+    protected void tearDown() throws Exception {
+        if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
+            mInCallCallbacks.getService().disconnectLastCall();
+            assertNumCalls(mInCallCallbacks.getService(), 0);
+        }
+        super.tearDown();
+    }
+
+    public void testIncomingCallShortPress_acceptsCall() throws Exception {
+        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        verifyConnectionForIncomingCall();
+
+        final MockConnection connection = mConnectionCallbacks.incomingConnection;
+        final Call call = mInCallCallbacks.getService().getLastCall();
+        assertCallState(call, Call.STATE_RINGING);
+        assertConnectionState(connection, Connection.STATE_RINGING);
+
+        sendMediaButtonShortPress();
+        assertCallState(call,  Call.STATE_ACTIVE);
+        assertConnectionState(connection, Connection.STATE_ACTIVE);
+    }
+
+    public void testIncomingCallLongPress_rejectsCall() throws Exception {
+        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        verifyConnectionForIncomingCall();
+
+        final MockConnection connection = mConnectionCallbacks.incomingConnection;
+        final Call call = mInCallCallbacks.getService().getLastCall();
+        assertCallState(call, Call.STATE_RINGING);
+        assertConnectionState(connection, Connection.STATE_RINGING);
+
+        sendMediaButtonLongPress();
+        assertCallState(call, Call.STATE_DISCONNECTED);
+        assertConnectionState(connection, Connection.STATE_DISCONNECTED);
+    }
+
+    public void testInCallShortPress_togglesMute() throws Exception {
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final MockInCallService incallService = mInCallCallbacks.getService();
+
+        // Verify that sending short presses in succession toggles the mute state of the
+        // connection.
+        // Before the audio state is changed for the first time, the connection might not
+        // know about its audio state yet.
+        assertMuteState(incallService, false);
+        sendMediaButtonShortPress();
+        assertMuteState(connection, true);
+        assertMuteState(incallService, true);
+        sendMediaButtonShortPress();
+        assertMuteState(connection, false);
+        assertMuteState(incallService, false);
+    }
+
+    public void testInCallLongPress_hangupCall() throws Exception {
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final Call call = mInCallCallbacks.getService().getLastCall();
+        assertCallState(call, Call.STATE_ACTIVE);
+        assertConnectionState(connection, Connection.STATE_ACTIVE);
+
+        sendMediaButtonLongPress();
+        assertCallState(call, Call.STATE_DISCONNECTED);
+        assertConnectionState(connection, Connection.STATE_DISCONNECTED);
+    }
+
+    public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+        assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+    }
+
+    private void sendMediaButtonShortPress() throws Exception {
+        sendMediaButtonPress(false /* longPress */);
+    }
+
+    private void sendMediaButtonLongPress() throws Exception {
+        sendMediaButtonPress(true /* longPress */);
+    }
+
+    private void sendMediaButtonPress(boolean longPress) throws Exception {
+        final String command = "input keyevent " + (longPress ? "--longpress" : "--shortpress")
+                + " KEYCODE_HEADSETHOOK";
+        TestUtils.executeShellCommand(getInstrumentation(), command);
+    }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
index 0527e9a..145cc84 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
@@ -299,7 +299,7 @@
             new ShortCodeTest("it", "112", CATEGORY_NOT_SHORT_CODE),
             new ShortCodeTest("it", "116117", CATEGORY_FREE_SHORT_CODE),
             new ShortCodeTest("it", "4567", CATEGORY_NOT_SHORT_CODE),
-            new ShortCodeTest("it", "48000", CATEGORY_PREMIUM_SHORT_CODE),
+            new ShortCodeTest("it", "48000", CATEGORY_FREE_SHORT_CODE),
             new ShortCodeTest("it", "45678", CATEGORY_PREMIUM_SHORT_CODE),
             new ShortCodeTest("it", "56789", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
             new ShortCodeTest("it", "456789", CATEGORY_NOT_SHORT_CODE),
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 8575c32..2be1dcb 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -81,9 +81,11 @@
                 mListener = new PhoneStateListener() {
                     @Override
                     public void onCellLocationChanged(CellLocation location) {
-                        synchronized (mLock) {
-                            mOnCellLocationChangedCalled = true;
-                            mLock.notify();
+                        if(!mOnCellLocationChangedCalled) {
+                            synchronized (mLock) {
+                                mOnCellLocationChangedCalled = true;
+                                mLock.notify();
+                            }
                         }
                     }
                 };
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
old mode 100644
new mode 100755
index d267d63..3c6028f
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -115,6 +115,9 @@
             // Wait for things to settle.
             getUiDevice().waitForIdle();
 
+            // Wait for Activity draw finish
+            getInstrumentation().waitForIdleSync();
+
             // Find the application window.
             final int windowId = findAppWindowId(uiAutomation.getWindows());
             assertTrue(windowId >= 0);