Merge "Update Photos App Helper to improve dismiss initial dialog" into nyc-dev
diff --git a/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractStandardAppHelper.java b/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractStandardAppHelper.java
index f16aaac..93fd1fe 100644
--- a/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractStandardAppHelper.java
+++ b/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractStandardAppHelper.java
@@ -56,6 +56,7 @@
         int maxBacks = 4;
         while (!mDevice.hasObject(mLauncherStrategy.getWorkspaceSelector()) && maxBacks > 0) {
             mDevice.pressBack();
+            mDevice.waitForIdle();
             maxBacks--;
         }
 
diff --git a/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractYouTubeHelper.java b/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractYouTubeHelper.java
index 0f7e7ed..aa0c641 100644
--- a/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractYouTubeHelper.java
+++ b/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractYouTubeHelper.java
@@ -45,13 +45,6 @@
     }
 
     /**
-     * Setup expectations: YouTube is on the home page.
-     *
-     * This method selects the first video and blocks until the video is playing.
-     */
-    public abstract void playFirstVideo();
-
-    /**
      * Setup expectations: YouTube app is open.
      *
      * This method keeps pressing the back button until YouTube is on the home page.
@@ -66,6 +59,28 @@
     public abstract void goToSearchPage();
 
     /**
+     * Setup expectations: YouTube is on the non-fullscreen video player page.
+     *
+     * This method changes the video player to fullscreen mode. Has no effect if the video player
+     * is already in fullscreen mode.
+     */
+    public abstract void goToFullscreenMode();
+
+    /**
+     * Setup expectations: YouTube is on the home page.
+     *
+     * This method selects a video on the home page and blocks until the video is playing.
+     */
+    public abstract void playHomePageVideo();
+
+    /**
+     * Setup expectations: YouTube is on the search results page.
+     *
+     * This method selects a search result video and blocks until the video is playing.
+     */
+    public abstract void playSearchResultPageVideo();
+
+    /**
      * Setup expectations: Recently opened a video in the YouTube app.
      *
      * This method blocks until the video has loaded.
@@ -96,17 +111,9 @@
     public abstract void setVideoQuality(VideoQuality quality);
 
     /**
-     * Setup expectations: YouTube is on the non-fullscreen video player page.
-     *
-     * This method changes the video player to fullscreen mode. Has no effect if the video player
-     * is already in fullscreen mode.
-     */
-    public abstract void goToFullscreenMode();
-
-    /**
      * Setup expectations: YouTube is on the video player page.
      *
-     * This method plays the video if it is paused.
+     * This method resumes the video if it is paused.
      */
-    public abstract void playVideo();
+    public abstract void resumeVideo();
 }
diff --git a/libraries/facebook-app-helper/src/android/platform/test/helpers/FacebookHelperImpl.java b/libraries/facebook-app-helper/src/android/platform/test/helpers/FacebookHelperImpl.java
index bd24f42..b7f39f6 100644
--- a/libraries/facebook-app-helper/src/android/platform/test/helpers/FacebookHelperImpl.java
+++ b/libraries/facebook-app-helper/src/android/platform/test/helpers/FacebookHelperImpl.java
@@ -31,18 +31,19 @@
 public class FacebookHelperImpl extends AbstractFacebookHelper {
     private static final String TAG = "android.platform.test.helpers.FacebookHelperImpl";
 
-    private static final String UI_HOME_PAGE_CONTAINER_ID = "tab_content_viewpager";
+    private static final String UI_HOME_PAGE_CONTAINER_ID = "cs7";
     private static final String UI_LOADING_VIEW_ID = "loading_view";
-    private static final String UI_LOGIN_BUTTON_ID = "login_login";
-    private static final String UI_LOGIN_PASSWORD_ID = "login_password";
-    private static final String UI_LOGIN_ROOT_ID = "login_root";
-    private static final String UI_LOGIN_USERNAME_ID = "login_username";
-    private static final String UI_NEWS_FEED_TAB_ID = "news_feed_tab";
-    private static final String UI_NEWS_FEED_TAB_SELECTED_DESC = "Selected";
+    private static final String UI_LOGIN_BUTTON_ID = "bjb";
+    private static final String UI_LOGIN_PASSWORD_ID = "bj_";
+    private static final String UI_LOGIN_ROOT_ID = "bj6";
+    private static final String UI_LOGIN_USERNAME_ID = "bj8";
+    private static final String UI_NEWS_FEED_TAB_ID = "a0";
+    private static final String UI_NEWS_FEED_TAB_SELECTED_DESC = "News";
     private static final String UI_PACKAGE_NAME = "com.facebook.katana";
-    private static final String UI_POST_BUTTON_ID = "primary_named_button";
-    private static final String UI_STATUS_TEXT_ID = "status_text";
-    private static final String UI_STATUS_UPDATE_BUTTON_ID = "feed_composer_status_button";
+    private static final String UI_POST_BUTTON_ID = "rk";
+    private static final String UI_STATUS_TEXT_ID = "cmk";
+    private static final String UI_STATUS_UPDATE_BUTTON_ID = "bmp";
+    private static final String UI_LOGIN_ONE_TAP = "sc";
 
     private static final long UI_LOGIN_WAIT = 30000;
     private static final long UI_NAVIGATION_WAIT = 10000;
@@ -174,6 +175,8 @@
         statusUpdateButton.click();
         mDevice.wait(Until.findObject(
                 By.res(UI_PACKAGE_NAME, UI_STATUS_TEXT_ID)), UI_NAVIGATION_WAIT);
+
+        getStatusTextField().click();
     }
 
     private UiObject2 getStatusTextField() {
@@ -266,6 +269,14 @@
         usernameTextField.setText(username);
         passwordTextField.setText(password);
         loginButton.click();
+
+        // Check if one tap login screen is prompted and click on it
+        UiObject2 oneTapLogin = mDevice.wait(Until.findObject(
+                By.res(UI_PACKAGE_NAME, UI_LOGIN_ONE_TAP)), UI_NAVIGATION_WAIT);
+        if (oneTapLogin != null) {
+            oneTapLogin.click();
+        }
+
         mDevice.wait(Until.findObject(
                 By.res(UI_PACKAGE_NAME, UI_HOME_PAGE_CONTAINER_ID)), UI_NAVIGATION_WAIT);
         // Wait for user content to load after logging in
diff --git a/libraries/play-movies-app-helper/src/android/platform/test/helpers/PlayMoviesHelperImpl.java b/libraries/play-movies-app-helper/src/android/platform/test/helpers/PlayMoviesHelperImpl.java
index 263cc85..99830f5 100644
--- a/libraries/play-movies-app-helper/src/android/platform/test/helpers/PlayMoviesHelperImpl.java
+++ b/libraries/play-movies-app-helper/src/android/platform/test/helpers/PlayMoviesHelperImpl.java
@@ -19,6 +19,7 @@
 import android.app.Instrumentation;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.SystemClock;
+import android.platform.test.helpers.exceptions.UnknownUiException;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.Configurator;
@@ -31,12 +32,14 @@
 
 import java.util.regex.Pattern;
 
-import junit.framework.Assert;
-
 public class PlayMoviesHelperImpl extends AbstractPlayMoviesHelper {
     private static final String LOG_TAG = PlayMoviesHelperImpl.class.getSimpleName();
-    private static final String UI_PACKAGE = "com.google.android.videos";
 
+    private static final String UI_PACKAGE = "com.google.android.videos";
+    private static final String UI_NAV_DRAWER_ID = "play_drawer_list";
+    private static final String UI_MOVIE_LIST_ID = "play_header_listview";
+
+    private static final int SEARCH_MOVIES_SCROLL_RETRY = 4;
     private static final long APP_INIT_WAIT = 5000;
 
     private boolean mIsVersion3p8 = false;
@@ -69,7 +72,7 @@
      */
     @Override
     public String getPackage() {
-        return "com.google.android.videos";
+        return UI_PACKAGE;
     }
 
     /**
@@ -126,12 +129,15 @@
     public void openMoviesTab() {
         // Navigate to the Movies tab through the Navigation drawer
         openNavigationDrawer();
-        UiObject2 libraryButton = mDevice.findObject(By.text("My Library").clickable(true));
+        Pattern myLibraryPattern = Pattern.compile("My Library", Pattern.CASE_INSENSITIVE);
+        UiObject2 libraryButton = mDevice.findObject(By.text(myLibraryPattern).clickable(true));
         libraryButton.click();
         waitForNavigationDrawerClose();
         // Select the Movies tab if necessary
-        UiObject2 moviesTab = mDevice.findObject(By.text("MY MOVIES"));
-        Assert.assertNotNull("Unable to find movies tab", moviesTab);
+        UiObject2 moviesTab = getMoviesTab();
+        if (moviesTab == null) {
+            throw new UnknownUiException("Unable to find the movies tab.");
+        }
         if (!moviesTab.isSelected()) {
             moviesTab.click();
             mDevice.waitForIdle();
@@ -143,16 +149,38 @@
      */
     @Override
     public void playMovie(String name) {
-        UiObject2 title = mDevice.findObject(By.textContains(name));
-        Assert.assertNotNull(String.format("Failed to find movie by name %s", name), title);
+        UiObject2 title = null;
+        for (int retry = 0; retry < SEARCH_MOVIES_SCROLL_RETRY; retry++) {
+            title = mDevice.findObject(By.textContains(name));
+            if (title == null) {
+                UiObject2 scroller = mDevice.findObject(By.res(UI_PACKAGE, UI_MOVIE_LIST_ID));
+                if (scroller != null) {
+                    scroller.scroll(Direction.DOWN, 1.0f);
+                }
+            }
+        }
+        if (title == null) {
+            throw new IllegalArgumentException(
+                    String.format("Failed to find movie by name %s", name));
+        }
         title.click();
         UiObject2 play = mDevice.wait(Until.findObject(By.res(UI_PACKAGE, "play")), 5000);
-        Assert.assertNotNull("Failed to find play button", play);
+        if (play == null) {
+            throw new UnknownUiException("Failed to find the play button.");
+        }
         play.click();
         mDevice.waitForIdle();
     }
 
+    private boolean isNavigationDrawerOpen () {
+        return mDevice.hasObject(By.res(UI_PACKAGE, UI_NAV_DRAWER_ID));
+    }
+
     private void openNavigationDrawer() {
+        if (isNavigationDrawerOpen()) {
+            return;
+        }
+
         UiObject2 backButton = mDevice.findObject(By.pkg(getPackage()).desc("Navigate up"));
         if (backButton != null) {
             backButton.click();
@@ -160,7 +188,9 @@
         }
 
         UiObject2 navButton = mDevice.findObject(By.desc("Show navigation drawer"));
-        Assert.assertNotNull("Unable to find navigation drawer button", navButton);
+        if (navButton == null) {
+            throw new UnknownUiException("Unable to find the navigation drawer button.");
+        }
         navButton.click();
         waitForNavigationDrawerOpen();
     }
@@ -172,4 +202,14 @@
     private void waitForNavigationDrawerClose() {
         mDevice.wait(Until.gone(By.text("Settings").clickable(true)), 2500);
     }
+
+    private UiObject2 getMoviesTab() {
+        Pattern moviesText = Pattern.compile("MY MOVIES", Pattern.CASE_INSENSITIVE);
+        UiObject2 tab = mDevice.findObject(By.text(moviesText));
+        if (tab == null) {
+            moviesText = Pattern.compile("MOVIES", Pattern.CASE_INSENSITIVE);
+            tab = mDevice.findObject(By.text(moviesText));
+        }
+        return tab;
+    }
 }
diff --git a/libraries/youtube-app-helper/src/android/platform/test/helpers/YouTubeHelperImpl.java b/libraries/youtube-app-helper/src/android/platform/test/helpers/YouTubeHelperImpl.java
index ff6ef7e..2750f47 100644
--- a/libraries/youtube-app-helper/src/android/platform/test/helpers/YouTubeHelperImpl.java
+++ b/libraries/youtube-app-helper/src/android/platform/test/helpers/YouTubeHelperImpl.java
@@ -21,6 +21,8 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.os.SystemClock;
+import android.platform.test.helpers.exceptions.UiTimeoutException;
+import android.platform.test.helpers.exceptions.UnknownUiException;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.Direction;
@@ -29,7 +31,7 @@
 import android.support.test.uiautomator.UiObject2;
 import android.util.Log;
 
-import junit.framework.Assert;
+import java.util.regex.Pattern;
 
 public class YouTubeHelperImpl extends AbstractYouTubeHelper {
     private static final String TAG = AbstractYouTubeHelper.class.getSimpleName();
@@ -39,15 +41,17 @@
     private static final String UI_FULLSCREEN_BUTTON_DESC = "Enter fullscreen";
     private static final String UI_HELP_AND_FEEDBACK_TEXT = "Help & feedback";
     private static final String UI_HOME_BUTTON_DESC = "Home";
-    private static final String UI_VIDEO_PLAYER_ID = "watch_player";
+    private static final String UI_HOME_PAGE_VIDEO_ID = "event_item";
+    private static final String UI_VIDEO_INFO_VIEW_ID = "video_info_view";
     private static final String UI_PACKAGE_NAME = "com.google.android.youtube";
     private static final String UI_PLAY_VIDEO_DESC = "Play video";
     private static final String UI_PROGRESS_ID = "load_progress";
+    private static final String UI_RESULT_FILTER_ID = "menu_filter_results";
     private static final String UI_SEARCH_BUTTON_ID = "menu_search";
     private static final String UI_SEARCH_EDIT_TEXT_ID = "search_edit_text";
     private static final String UI_SELECT_DIALOG_LISTVIEW_ID = "select_dialog_listview";
     private static final String UI_TRENDING_BUTTON_DESC = "Trending";
-    private static final String UI_VIDEO_CARD_ID = "video_info_view";
+    private static final String UI_VIDEO_PLAYER_ID = "watch_player";
     private static final String UI_VIDEO_PLAYER_OVERFLOW_BUTTON_ID = "player_overflow_button";
     private static final String UI_VIDEO_PLAYER_PLAY_PAUSE_REPLAY_BUTTON_ID =
             "player_control_play_pause_replay_button";
@@ -112,28 +116,64 @@
      * {@inheritDoc}
      */
     @Override
-    public void playFirstVideo() {
+    public void playHomePageVideo() {
+        if (!isOnHomePage()) {
+            throw new IllegalStateException("YouTube is not on the home page.");
+        }
+
+        if (hasConnectionEstablishedMessage()) {
+            pressGoOnline();
+        }
+
         for (int i = 0; i < 3; i++) {
-            UiObject2 video = getFirstVideo();
+            UiObject2 video = getPlayableVideo();
             if (video != null) {
                 video.click();
                 waitForVideoToLoad(UI_NAVIGATION_WAIT);
                 return;
+            } else {
+                scrollHomePage(Direction.DOWN);
             }
         }
 
         if (isLoading()) {
-            Assert.fail("Timed out waiting for video search result to load.");
-        } else {
-            Assert.fail("YouTube does not support playing videos from this page or an " +
-                    "unexpected automation failure occurred.");
+            throw new UiTimeoutException("Timed out waiting for video search results.");
         }
+
+        throw new UnknownUiException("Unsuccessful attempt playing home page video.");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void playSearchResultPageVideo() {
+        if (!isOnSearchResultsPage()) {
+            throw new IllegalStateException("YouTube is not on the home page.");
+        }
+
+        for (int i = 0; i < 3; i++) {
+            UiObject2 video = getPlayableVideo();
+            if (video != null) {
+                video.click();
+                waitForVideoToLoad(UI_NAVIGATION_WAIT);
+                return;
+            } else {
+                scrollSearchResultsPage(Direction.DOWN);
+            }
+        }
+
+        throw new UnknownUiException("Unsuccessful attempt playing search result video.");
     }
 
     private UiObject2 getHomePageContainer() {
         return mDevice.findObject(By.res(UI_PACKAGE_NAME, UI_HOME_CONTAINER_ID));
     }
 
+    private UiObject2 getSearchResultsPageContainer() {
+        return getHomePageContainer();
+    }
+
     private UiObject2 getHomeButton() {
         return mDevice.findObject(By.pkg(UI_PACKAGE_NAME).desc(UI_HOME_BUTTON_DESC));
     }
@@ -151,16 +191,35 @@
     }
 
     private void scrollHomePage(Direction dir) {
+        if (dir == Direction.RIGHT || dir == Direction.LEFT) {
+            throw new IllegalArgumentException("Can only scroll up and down.");
+        }
+
         UiObject2 scrollContainer = getHomePageContainer();
         if (scrollContainer != null) {
             scrollContainer.scroll(dir, 1.0f);
             mDevice.waitForIdle();
         } else {
-            Assert.fail("No valid scrolling mechanism found.");
+            throw new UnknownUiException("No scrolling mechanism found.");
+        }
+    }
+
+    private void scrollSearchResultsPage(Direction dir) {
+        if (dir == Direction.RIGHT || dir == Direction.LEFT) {
+            throw new IllegalArgumentException("Can only scroll up and down.");
+        }
+
+        UiObject2 scrollContainer = getSearchResultsPageContainer();
+        if (scrollContainer != null) {
+            scrollContainer.scroll(dir, 1.0f);
+            mDevice.waitForIdle();
+        } else {
+            throw new UnknownUiException("No scrolling mechanism found.");
         }
     }
 
     private boolean isLoading() {
+        // TODO: Is loading what? Requires more documentation.
         return mDevice.hasObject(By.res(UI_PACKAGE_NAME, UI_PROGRESS_ID));
     }
 
@@ -179,8 +238,19 @@
         return (accountButton != null && accountButton.isSelected());
     }
 
-    private UiObject2 getFirstVideo() {
-        return mDevice.findObject(By.res(UI_PACKAGE_NAME, UI_VIDEO_CARD_ID));
+    private boolean isOnSearchResultsPage() {
+        // Simplest way to identify search result page is the result filter button.
+        UiObject2 resultFilterButton =
+                mDevice.findObject(By.res(UI_PACKAGE_NAME, UI_RESULT_FILTER_ID));
+        return (resultFilterButton != null);
+    }
+
+    private UiObject2 getPlayableVideo() {
+        UiObject2 video = mDevice.findObject(By.res(UI_PACKAGE_NAME, UI_HOME_PAGE_VIDEO_ID));
+        if (video == null) {
+            video = mDevice.findObject(By.res(UI_PACKAGE_NAME, UI_VIDEO_INFO_VIEW_ID));
+        }
+        return video;
     }
 
     /**
@@ -192,6 +262,7 @@
             By.res(UI_PACKAGE_NAME, UI_VIDEO_PLAYER_ID)), timeout);
     }
 
+
     /**
      * {@inheritDoc}
      */
@@ -202,13 +273,22 @@
             mDevice.pressBack();
             SystemClock.sleep(3000);
         }
+        // Get and press the home button
         UiObject2 homeButton = getHomeButton();
-        Assert.assertNotNull("Could not find home button", homeButton);
-
-        homeButton.click();
-        Assert.assertTrue("Not on home page after pressing home button",
-                mDevice.wait(Until.hasObject(By.pkg(UI_PACKAGE_NAME).desc(
-                UI_HOME_BUTTON_DESC).selected(true)), UI_NAVIGATION_WAIT));
+        if (homeButton == null) {
+            throw new UnknownUiException("Could not find home button.");
+        } else if (!homeButton.isSelected()) {
+            homeButton.click();
+            // Validate the home button is selected
+            if (!mDevice.wait(Until.hasObject(
+                    By.pkg(UI_PACKAGE_NAME).desc(UI_HOME_BUTTON_DESC).selected(true)),
+                    UI_NAVIGATION_WAIT)) {
+                throw new UnknownUiException("Not on home page after pressing home button.");
+            } else {
+                // Make sure the transition is complete
+                mDevice.waitForIdle();
+            }
+        }
     }
 
     /**
@@ -216,20 +296,55 @@
      */
     @Override
     public void goToSearchPage() {
+        if (!isOnHomePage()) {
+            throw new IllegalStateException("YouTube is not on the home page.");
+        }
+
         UiObject2 searchButton = getSearchButton();
         if (searchButton == null) {
-            UiObject2 homeButton = getHomeButton();
-            Assert.assertNotNull("Could not find home button", homeButton);
-
-            homeButton.click();
-            searchButton = mDevice.wait(Until.findObject(
-                    By.res(UI_PACKAGE_NAME, UI_SEARCH_BUTTON_ID)), UI_NAVIGATION_WAIT);
+            throw new UnknownUiException("Could not find search button.");
+        } else {
+            searchButton.click();
+            if (!mDevice.wait(Until.hasObject(
+                    By.res(UI_PACKAGE_NAME, UI_SEARCH_EDIT_TEXT_ID)), UI_NAVIGATION_WAIT)) {
+                throw new UnknownUiException("Not on search page after pressing search button.");
+            }
         }
-        Assert.assertNotNull("Could not find search button", searchButton);
-        searchButton.click();
-        Assert.assertTrue("Not on search page after pressing search button",
-                mDevice.wait(Until.hasObject(By.res(UI_PACKAGE_NAME, UI_SEARCH_EDIT_TEXT_ID)),
-                UI_NAVIGATION_WAIT));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void goToFullscreenMode() {
+        if (!isOnVideo()) {
+            throw new IllegalStateException("YouTube is not on a video page.");
+        }
+
+        if (getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            return;
+        }
+
+        UiObject2 fullscreenButton = null;
+        for (int retriesRemaining = 5; retriesRemaining > 0; --retriesRemaining) {
+            UiObject2 miniVideoPlayer = getVideoPlayer();
+            if (miniVideoPlayer == null) {
+                throw new UnknownUiException("Could not find mini video player.");
+            }
+
+            miniVideoPlayer.click();
+            SystemClock.sleep(1500);
+            fullscreenButton = getFullscreenButton();
+            if (fullscreenButton != null) {
+                fullscreenButton.click();
+                // TODO: Add a valid wait for fullscreen
+                break;
+            }
+        }
+
+        if (fullscreenButton == null) {
+            throw new UnknownUiException("Did not find a fullscreen button.");
+        }
     }
 
     private UiObject2 getVideoPlayer() {
@@ -260,7 +375,8 @@
      */
     @Override
     public boolean waitForSearchResults(long timeout) {
-        return mDevice.wait(Until.hasObject(By.res(UI_PACKAGE_NAME, UI_VIDEO_CARD_ID)), timeout);
+        return mDevice.wait(Until.hasObject(
+                By.res(UI_PACKAGE_NAME, UI_VIDEO_INFO_VIEW_ID)), timeout);
     }
 
     /**
@@ -268,32 +384,46 @@
      */
     @Override
     public void setVideoQuality(VideoQuality quality) {
-        Assert.assertTrue("Not on video player", isOnVideo());
+        if (!isOnVideo()) {
+            throw new IllegalStateException("YouTube is not on a video page.");
+        }
 
         UiObject2 overflowButton = getVideoPlayerOverflowButton();
+        // Open the mini video player
         if (overflowButton == null) {
             UiObject2 miniVideoPlayer = getVideoPlayer();
-            Assert.assertNotNull("Could not find video player", miniVideoPlayer);
+            if (miniVideoPlayer == null) {
+                throw new UnknownUiException("Could not find mini video player.");
+            }
 
             miniVideoPlayer.click();
             mDevice.wait(Until.findObject(By.res(
                 UI_PACKAGE_NAME, UI_VIDEO_PLAYER_OVERFLOW_BUTTON_ID)), UI_NAVIGATION_WAIT);
             overflowButton = getVideoPlayerOverflowButton();
         }
-        Assert.assertNotNull("Could not find overflow button", overflowButton);
+
+        if (overflowButton == null) {
+            throw new UnknownUiException("Could not find overflow button.");
+        }
 
         overflowButton.click();
         UiObject2 qualityButton = mDevice.wait(Until.findObject(
                 By.res(UI_PACKAGE_NAME, UI_VIDEO_PLAYER_QUALITY_BUTTON_ID)), UI_NAVIGATION_WAIT);
-        Assert.assertNotNull("Could not find video quality button", qualityButton);
+        if (qualityButton == null) {
+            throw new UnknownUiException("Could not find video quality button.");
+        }
 
         qualityButton.click();
         UiObject2 quality360pLabel = mDevice.wait(Until.findObject(By.text(
                 AbstractYouTubeHelper.VideoQuality.QUALITY_360p.getText())), UI_NAVIGATION_WAIT);
-        Assert.assertNotNull("Could not find 360p quality label", quality360pLabel);
+        if (quality360pLabel == null) {
+            throw new UnknownUiException("Could not find 360p quality label.");
+        }
 
         UiObject2 selectDialog = quality360pLabel.getParent();
-        Assert.assertNotNull("Could not find video quality dialog", selectDialog);
+        if (selectDialog == null) {
+            throw new UnknownUiException("Could not find video quality dialog.");
+        }
 
         UiObject2 qualityLabel = null;
         for (int retriesRemaining = 5; retriesRemaining > 0; --retriesRemaining) {
@@ -304,63 +434,60 @@
             selectDialog.scroll(Direction.DOWN, 1.0f);
             mDevice.waitForIdle();
         }
-        Assert.assertNotNull(String.format("Could not find quality %s label", quality.getText()),
-                qualityLabel);
+        if (qualityLabel == null) {
+            throw new UnknownUiException(
+                    String.format("Could not find quality %s label", quality.getText()));
+        }
 
         Log.v(TAG, String.format("Found quality %s label", quality.getText()));
         qualityLabel.click();
-        Assert.assertTrue("Could not find video player after selecting quality",
-                mDevice.wait(Until.hasObject(By.res(UI_PACKAGE_NAME, UI_VIDEO_PLAYER_ID)),
-                UI_NAVIGATION_WAIT));
+        if (!mDevice.wait(Until.hasObject(By.res(UI_PACKAGE_NAME, UI_VIDEO_PLAYER_ID)),
+                UI_NAVIGATION_WAIT)) {
+            throw new UnknownUiException("Did not find video player after selecting quality.");
+        }
     }
 
     private UiObject2 getFullscreenButton() {
         return mDevice.findObject(By.desc(UI_FULLSCREEN_BUTTON_DESC));
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void goToFullscreenMode() {
-        Assert.assertTrue(isOnVideo());
-
-        if (getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
-            return;
-        }
-
-        UiObject2 fullscreenButton = null;
-        for (int retriesRemaining = 5; retriesRemaining > 0; --retriesRemaining) {
-            UiObject2 miniVideoPlayer = getVideoPlayer();
-            Assert.assertNotNull("Could not find video player", miniVideoPlayer);
-
-            miniVideoPlayer.click();
-            SystemClock.sleep(1500);
-            fullscreenButton = getFullscreenButton();
-            if (fullscreenButton != null) {
-                fullscreenButton.click();
-                break;
-            }
-        }
-        Assert.assertNotNull("Could not find fullscreen button", fullscreenButton);
-    }
-
     private UiObject2 getPlayPauseReplayButton() {
         return mDevice.findObject(
             By.res(UI_PACKAGE_NAME, UI_VIDEO_PLAYER_PLAY_PAUSE_REPLAY_BUTTON_ID));
     }
 
-    public void playVideo() {
+    public void resumeVideo() {
         UiObject2 videoPlayer = getVideoPlayer();
-        Assert.assertNotNull("Could not find video player", videoPlayer);
+        if (videoPlayer == null) {
+            throw new UnknownUiException("Could not find video player.");
+        }
 
         videoPlayer.click();
         UiObject2 playPauseReplayButton = mDevice.wait(Until.findObject(By.res(UI_PACKAGE_NAME,
                 UI_VIDEO_PLAYER_PLAY_PAUSE_REPLAY_BUTTON_ID)), UI_NAVIGATION_WAIT);
-        Assert.assertNotNull("Could not find pause / play button", playPauseReplayButton);
+        if (playPauseReplayButton == null) {
+            throw new UnknownUiException("Could not find the pause/play button.");
+        }
 
         if (UI_PLAY_VIDEO_DESC.equals(playPauseReplayButton.getContentDescription())) {
             playPauseReplayButton.click();
         }
     }
+
+    private boolean hasConnectionEstablishedMessage() {
+        Pattern establishedMsg =
+                Pattern.compile("Connection established", Pattern.CASE_INSENSITIVE);
+        return mDevice.hasObject(By.res(UI_PACKAGE_NAME, "message").text(establishedMsg));
+    }
+
+    private void pressGoOnline() {
+        Pattern goOnlineMsg = Pattern.compile("Go online", Pattern.CASE_INSENSITIVE);
+        UiObject2 button = mDevice.findObject(By.res(UI_PACKAGE_NAME, "action").text(goOnlineMsg));
+        if (button != null) {
+            button.click();
+            mDevice.waitForIdle();
+        } else {
+            throw new UnknownUiException("Unable to find GO ONLINE button.");
+        }
+    }
 }
diff --git a/tests/androidbvt/Android.mk b/tests/androidbvt/Android.mk
index 0461dec..970a3df 100644
--- a/tests/androidbvt/Android.mk
+++ b/tests/androidbvt/Android.mk
@@ -17,8 +17,8 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_SDK_VERSION := current
-
+LOCAL_SDK_VERSION := system_current
+media_framework_app_base := frameworks/base/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator launcher-helper-lib
@@ -26,4 +26,4 @@
 LOCAL_PACKAGE_NAME := AndroidBvtTests
 LOCAL_CERTIFICATE := platform
 
-include $(BUILD_PACKAGE)
\ No newline at end of file
+include $(BUILD_PACKAGE)
diff --git a/tests/androidbvt/AndroidManifest.xml b/tests/androidbvt/AndroidManifest.xml
index d5962a5..c38bc88 100644
--- a/tests/androidbvt/AndroidManifest.xml
+++ b/tests/androidbvt/AndroidManifest.xml
@@ -17,16 +17,39 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.androidbvt">
-
     <uses-sdk android:minSdkVersion="19"
               android:targetSdkVersion="24" />
-    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-feature android:name="android.hardware.camera"
+                  android:required="true" />
+
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
+    <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
+        <activity android:label="MediaPlaybackTest"
+                android:name=".app.MediaPlaybackTestApp"
+                android:screenOrientation="landscape">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
     </application>
     <instrumentation
             android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/androidbvt/res/layout/surface_view.xml b/tests/androidbvt/res/layout/surface_view.xml
new file mode 100644
index 0000000..4999e5d
--- /dev/null
+++ b/tests/androidbvt/res/layout/surface_view.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="match_parent"
+  android:layout_height="match_parent"
+  android:orientation="vertical">
+
+  <FrameLayout
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+  <SurfaceView
+     android:id="@+id/surface_view"
+     android:layout_width="match_parent"
+     android:layout_height="match_parent"
+     android:layout_centerInParent="true"
+     />
+
+  <ImageView android:id="@+id/overlay_layer"
+     android:layout_width="0dip"
+     android:layout_height="392dip"/>
+
+  <VideoView
+   android:id="@+id/video_view"
+        android:layout_width="320px"
+        android:layout_height="240px"
+  />
+
+  </FrameLayout>
+
+</LinearLayout>
+
diff --git a/tests/androidbvt/res/raw/bbb.mkv b/tests/androidbvt/res/raw/bbb.mkv
new file mode 100644
index 0000000..e286e01
--- /dev/null
+++ b/tests/androidbvt/res/raw/bbb.mkv
Binary files differ
diff --git a/tests/androidbvt/src/com/android/androidbvt/ConnectivityWifiTests.java b/tests/androidbvt/src/com/android/androidbvt/ConnectivityWifiTests.java
index 834a705..c9bbd92 100644
--- a/tests/androidbvt/src/com/android/androidbvt/ConnectivityWifiTests.java
+++ b/tests/androidbvt/src/com/android/androidbvt/ConnectivityWifiTests.java
@@ -27,6 +27,7 @@
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.Until;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
 import junit.framework.TestCase;
@@ -37,6 +38,8 @@
 
 public class ConnectivityWifiTests extends TestCase {
     private final static String DEFAULT_PING_SITE = "www.google.com";
+    private final String NETWORK_ID = "AndroidAP";
+    private final String PASSWD = "androidwifi";
     private UiDevice mDevice;
     private WifiManager mWifiManager = null;
     private Context mContext = null;
@@ -106,6 +109,68 @@
     }
 
     /**
+     * Verifies WifiAp is by default disabled Then enable adn disable it
+     */
+    @LargeTest
+    @Suppress
+    public void testWifiTetheringDisableEnable() throws InterruptedException {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = NETWORK_ID;
+        config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+        config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
+        config.preSharedKey = PASSWD;
+        int counter;
+        try {
+            // disable wifiap
+            assertTrue("wifi hotspot not disabled by default",
+                    mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED);
+            // Enable wifiap
+            assertTrue("failed to disable wifi hotspot",
+                    mWifiManager.setWifiApEnabled(config, true));
+            Log.d("MyTestTag", "Now checkign wifi ap");
+            counter = 10;
+            while (--counter > 0
+                    && mWifiManager.getWifiApState() != WifiManager.WIFI_AP_STATE_ENABLED) {
+                Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
+            }
+            assertTrue("wifi hotspot not enabled",
+                    mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED);
+            // Navigate to Wireless Settings page and verify Wifi AP setting is on
+            Intent intent_as = new Intent(
+                    android.provider.Settings.ACTION_WIRELESS_SETTINGS);
+            mContext.startActivity(intent_as);
+            Thread.sleep(mABvtHelper.LONG_TIMEOUT);
+            mDevice.wait(Until.findObject(By.text("Tethering & portable hotspot")),
+                    mABvtHelper.LONG_TIMEOUT).click();
+            Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
+            assertTrue("Settings UI for Wifi AP is not ON",
+                    mDevice.wait(Until.hasObject(By.text("Portable hotspot AndroidAP active")),
+                            mABvtHelper.LONG_TIMEOUT));
+
+            mDevice.wait(Until.findObject(By.text("Portable Wi‑Fi hotspot")),
+                    mABvtHelper.LONG_TIMEOUT).click();
+            assertTrue("Wifi ap disable call fails", mWifiManager.setWifiApEnabled(config,
+                    false));
+            counter = 5;
+            while (--counter > 0
+                    && mWifiManager.getWifiApState() != WifiManager.WIFI_AP_STATE_DISABLED) {
+                Thread.sleep(mABvtHelper.LONG_TIMEOUT);
+            }
+            assertTrue("wifi hotspot not enabled",
+                    mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED);
+            Thread.sleep(mABvtHelper.LONG_TIMEOUT * 2);
+        } finally {
+            assertTrue("Wifi enable call fails", mWifiManager
+                    .enableNetwork(mWifiManager.getConnectionInfo().getNetworkId(), false));
+            counter = 10;
+            while (--counter > 0 && !mWifiManager.isWifiEnabled()) {
+                Thread.sleep(mABvtHelper.LONG_TIMEOUT);
+            }
+            assertTrue("Wifi isn't enabled", mWifiManager.isWifiEnabled());
+        }
+    }
+
+    /**
      * Checks if wifi connection is active by sending an HTTP request, check for HTTP_OK
      */
     private boolean isWifiConnected() throws InterruptedException {
diff --git a/tests/androidbvt/src/com/android/androidbvt/MediaCaptureTests.java b/tests/androidbvt/src/com/android/androidbvt/MediaCaptureTests.java
new file mode 100644
index 0000000..518a49d
--- /dev/null
+++ b/tests/androidbvt/src/com/android/androidbvt/MediaCaptureTests.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 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.androidbvt;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import junit.framework.TestCase;
+import java.io.File;
+import java.util.regex.Pattern;
+
+/**
+ * Basic tests for the Camera app.
+ */
+public class MediaCaptureTests extends TestCase {
+    private static final int CAPTURE_TIMEOUT = 6000;
+    private static final String DESC_BTN_CAPTURE_PHOTO = "Capture photo";
+    private static final String DESC_BTN_CAPTURE_VIDEO = "Capture video";
+    private static final String DESC_BTN_DONE = "Done";
+    private static final String DESC_BTN_PHOTO_MODE = "Open photo mode";
+    private static final String DESC_BTN_VIDEO_MODE = "Open video mode";
+    private static final int FILE_CHECK_ATTEMPTS = 5;
+    private static final int VIDEO_LENGTH = 2000;
+
+    private UiDevice mDevice;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mDevice.freezeRotation();
+        // if there are any dialogues that pop up, dismiss them
+        UiObject2 maybeOkButton = mDevice.wait(Until.findObject(By.res("android:id/ok_button")),
+                CAPTURE_TIMEOUT);
+        if (maybeOkButton != null) {
+            maybeOkButton.click();
+        }
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mDevice.unfreezeRotation();
+        super.tearDown();
+    }
+
+    /**
+     * Test that the device can capture a photo.
+     */
+    @LargeTest
+    public void testPhotoCapture() {
+        runCaptureTest(new Intent(MediaStore.ACTION_IMAGE_CAPTURE), "smoke.jpg", false);
+    }
+
+    /**
+     * Test that the device can capture a video.
+     */
+    @LargeTest
+    public void testVideoCapture() {
+        runCaptureTest(new Intent(MediaStore.ACTION_VIDEO_CAPTURE), "smoke.avi", true);
+    }
+
+    private void runCaptureTest(Intent intent, String tmpName, boolean isVideo) {
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        if (intent.resolveActivity(
+                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()) != null) {
+            File outputFile = null;
+            try {
+                outputFile = new File(Environment
+                        .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), tmpName);
+                intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputFile));
+                InstrumentationRegistry.getInstrumentation().getContext().startActivity(intent);
+                switchCaptureMode(isVideo);
+                pressCaptureButton(isVideo);
+                if (isVideo) {
+                    Thread.sleep(VIDEO_LENGTH);
+                    pressCaptureButton(isVideo);
+                }
+                Thread.sleep(1000);
+                pushButton(DESC_BTN_DONE);
+                long fileLength = outputFile.length();
+                for (int i=0; i<FILE_CHECK_ATTEMPTS; i++) {
+                    if ((fileLength = outputFile.length()) > 0) {
+                        break;
+                    }
+                    Thread.sleep(1000);
+                }
+                assertTrue(fileLength > 0);
+            } catch (InterruptedException e) {
+                fail(e.getLocalizedMessage());
+            } finally {
+                if (outputFile != null) {
+                    outputFile.delete();
+                }
+            }
+        }
+    }
+
+    private void switchCaptureMode(boolean isVideo) {
+        if (isVideo) {
+            pushButton(DESC_BTN_VIDEO_MODE);
+        } else {
+            pushButton(DESC_BTN_PHOTO_MODE);
+        }
+    }
+
+    private void pressCaptureButton(boolean isVideo) {
+        if (isVideo) {
+            pushButton(DESC_BTN_CAPTURE_VIDEO);
+        } else {
+            pushButton(DESC_BTN_CAPTURE_PHOTO);
+        }
+    }
+
+    private void pushButton(String desc) {
+        Pattern pattern = Pattern.compile(desc, Pattern.CASE_INSENSITIVE);
+        UiObject2 doneBtn = mDevice.wait(Until.findObject(By.desc(pattern)), CAPTURE_TIMEOUT);
+        if (null != doneBtn) {
+            doneBtn.clickAndWait(Until.newWindow(), 500);
+        }
+    }
+}
diff --git a/tests/androidbvt/src/com/android/androidbvt/MediaPlaybackTests.java b/tests/androidbvt/src/com/android/androidbvt/MediaPlaybackTests.java
new file mode 100644
index 0000000..474e820
--- /dev/null
+++ b/tests/androidbvt/src/com/android/androidbvt/MediaPlaybackTests.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 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.androidbvt;
+
+import android.media.MediaPlayer;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import com.android.androidbvt.app.MediaPlaybackTestApp;
+
+/**
+ * Basic tests for video playback
+ */
+public class MediaPlaybackTests extends ActivityInstrumentationTestCase2<MediaPlaybackTestApp> {
+
+    private static final String TAG = "MediaPlaybackTest";
+    private static final int LOOP_START_BUFFER_MS = 10000;
+    private static final int PLAY_BUFFER_MS = 2000;
+    private final Object mCompletionLock = new Object();
+    private final Object mLooperLock = new Object();
+    private boolean mPlaybackSucceeded = false;
+    private boolean mPlaybackError = false;
+    private Looper mLooper;
+    private MediaPlayer mPlayer;
+
+    public MediaPlaybackTests() {
+        super(MediaPlaybackTestApp.class);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        // start activity
+        getActivity();
+    }
+
+    @LargeTest
+    public void testVideoPlayback() {
+        // start the MediaPlayer on a Looper thread, so it does not deadlock itself
+        new Thread() {
+            @Override
+            public void run() {
+                Looper.prepare();
+                mLooper = Looper.myLooper();
+                mPlayer = MediaPlayer.create(getInstrumentation().getContext(), R.raw.bbb);
+                mPlayer.setDisplay(getActivity().getSurfaceHolder());
+                synchronized (mLooperLock) {
+                    mLooperLock.notify();
+                }
+                Looper.loop();
+            }
+        }.start();
+        // make sure the looper is really started before we proceed
+        synchronized (mLooperLock) {
+            try {
+                mLooperLock.wait(LOOP_START_BUFFER_MS);
+            } catch (InterruptedException e) {
+                fail("Loop thread start was interrupted");
+            }
+        }
+        mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+            @Override
+            public boolean onError(MediaPlayer mp, int what, int extra) {
+                mPlaybackError = true;
+                mp.reset();
+                return true;
+            }
+        });
+        mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(MediaPlayer mp) {
+                synchronized (mCompletionLock) {
+                    Log.w(TAG, "Hit onCompletion!");
+                    mPlaybackSucceeded = true;
+                    mCompletionLock.notifyAll();
+                }
+            }
+        });
+        mPlayer.start();
+        int duration = mPlayer.getDuration();
+        int currentPosition = mPlayer.getCurrentPosition();
+        synchronized (mCompletionLock) {
+            try {
+                mCompletionLock.wait(duration - currentPosition + PLAY_BUFFER_MS);
+            } catch (InterruptedException e) {
+                fail("Wait for playback was interrupted");
+            }
+        }
+        mLooper.quit();
+        mPlayer.release();
+        assertFalse(mPlaybackError);
+        assertTrue(mPlaybackSucceeded);
+    }
+}
diff --git a/tests/androidbvt/src/com/android/androidbvt/SysUIGSATests.java b/tests/androidbvt/src/com/android/androidbvt/SysUIGSATests.java
new file mode 100644
index 0000000..dd7a9cf
--- /dev/null
+++ b/tests/androidbvt/src/com/android/androidbvt/SysUIGSATests.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 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.androidbvt;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.KeyEvent;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+/**
+ * Contains tests for features that are loosely coupled with Android system for sanity
+ */
+public class SysUIGSATests extends TestCase {
+    private final String QSB_PKG = "com.google.android.googlequicksearchbox";
+    private UiAutomation mUiAutomation = null;
+    private UiDevice mDevice;
+    private Context mContext = null;
+    private AndroidBvtHelper mABvtHelper = null;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mDevice.setOrientationNatural();
+        mContext = InstrumentationRegistry.getTargetContext();
+        mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        mABvtHelper = AndroidBvtHelper.getInstance(mDevice, mContext, mUiAutomation);
+        mDevice.pressMenu();
+        mDevice.pressHome();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mDevice.pressHome();
+        mDevice.unfreezeRotation();
+        super.tearDown();
+    }
+
+    /**
+     * Ensures search via QSB searches both web and device apps Suuggested texts starts with
+     * searched text Remembers searched item, suggests as top suggestion next time
+     */
+    @LargeTest
+    public void testGoogleQuickSearchBar() throws InterruptedException {
+        final String TextToSearch = "co";
+        UiObject2 searchBox = null;
+        int counter = 5;
+        while (--counter > 0
+                && ((searchBox = mDevice.wait(Until.findObject(By.res(QSB_PKG, "search_box")),
+                        mABvtHelper.SHORT_TIMEOUT)) == null)) {
+            Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
+            mDevice.pressHome();
+            mDevice.pressSearch();
+        }
+        mDevice.wait(Until.findObject(By.res(QSB_PKG, "search_box")),
+                mABvtHelper.LONG_TIMEOUT).setText(TextToSearch);
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
+        // make the IME down
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_BACK);
+        // searching for 'co' will result from web, as well as 'Contacts' app. So there should be
+        // more than 1 container
+        UiObject2 searchSuggestionsContainer = mDevice.wait(Until.findObject(By.res(
+                QSB_PKG, "search_suggestions_container")), mABvtHelper.LONG_TIMEOUT);
+        assertTrue("QS suggestion should have more than 1 container",
+                searchSuggestionsContainer.getChildCount() > 1);
+        UiObject2 searchSuggestions = mDevice.wait(Until.findObject(By.res(
+                QSB_PKG, "search_suggestions_web")), mABvtHelper.LONG_TIMEOUT);
+        assertNotNull(
+                "Web Search suggestions shouldn't be null & should have more than 1 suggestions",
+                searchSuggestions != null && searchSuggestions.getChildCount() > 1);
+        List<UiObject2> suggestions = mDevice.wait(Until.findObjects(By.res(QSB_PKG, "text_1")),
+                mABvtHelper.LONG_TIMEOUT);
+        assertNotNull("Contacts app should be found", mDevice.wait(Until.findObject(
+                By.res(QSB_PKG, "text_1").text("Contacts")), mABvtHelper.LONG_TIMEOUT));
+        String topSuggestedText = suggestions.get(0).getText();
+        suggestions.get(0).clickAndWait(Until.newWindow(), mABvtHelper.LONG_TIMEOUT);
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
+        // Search again and ensure last searched item showed as top suggestion
+        mDevice.pressHome();
+        Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
+        mDevice.pressSearch();
+        String currentTopSuggestion = mDevice.wait(Until.findObjects(By.res(QSB_PKG, "text_1")),
+                mABvtHelper.LONG_TIMEOUT).get(0).getText();
+        assertTrue("Previous searched item isn't top suggested word",
+                topSuggestedText.toLowerCase().equals(topSuggestedText.toLowerCase()));
+    }
+
+    /**
+     * Ensures if any account is opted in GoogleNow, Google-assist offers card on long home press
+     */
+    @LargeTest
+    public void testGoogleAssist() throws InterruptedException {
+        mDevice.wait(Until.findObject(By.res(QSB_PKG, "search_plate")),
+                mABvtHelper.SHORT_TIMEOUT).click();
+        Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
+        UiObject2 getStarted = mDevice.wait(Until.findObject(By.text("GET STARTED")),
+                mABvtHelper.SHORT_TIMEOUT);
+        if (getStarted != null) {
+            getStarted.clickAndWait(Until.newWindow(), mABvtHelper.SHORT_TIMEOUT);
+            mDevice.wait(Until.findObject(By.res(QSB_PKG, "text_container")),
+                    mABvtHelper.SHORT_TIMEOUT).swipe(Direction.UP, 1.0f);
+            mDevice.wait(Until.findObject(By.text("YES, I’M IN")),
+                    mABvtHelper.SHORT_TIMEOUT)
+                    .clickAndWait(Until.newWindow(), mABvtHelper.SHORT_TIMEOUT);
+        }
+        // Search for Paris and click on first suggested text
+        mDevice.wait(Until.findObject(By.res(QSB_PKG, "text_container")),
+                mABvtHelper.LONG_TIMEOUT).setText("Paris");
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
+        List<UiObject2> suggestedTexts = null;
+        int counter = 5;
+        while (--counter > 0
+                && ((suggestedTexts = mDevice.wait(Until.findObjects(By.res(QSB_PKG, "text_1")),
+                        mABvtHelper.LONG_TIMEOUT)) == null)) {
+            Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
+        }
+        assertNotNull("Suggested text shouldn't be null", suggestedTexts);
+        UiObject2 itemToClick = suggestedTexts.get(0);
+        for (UiObject2 item : suggestedTexts) {
+            if (item.getText().toLowerCase().equals("paris")) {
+                itemToClick = item;
+            }
+        }
+        itemToClick.clickAndWait(Until.newWindow(), mABvtHelper.SHORT_TIMEOUT);
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
+        // Now long press home to load assist layer
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_ASSIST);
+        UiObject2 optInYes = mDevice.wait(
+                Until.findObject(By.res(QSB_PKG, "screen_assist_opt_in_yes")),
+                mABvtHelper.SHORT_TIMEOUT);
+        if (optInYes != null) {
+            optInYes.clickAndWait(Until.newWindow(), mABvtHelper.SHORT_TIMEOUT);
+        }
+        // Ensure some cards are loaded
+        // Note card's content isn't verified
+        counter = 5;
+        UiObject2 cardContainer = null;
+        while (--counter > 0 && ((cardContainer = mDevice.wait(
+                Until.findObject(By.res(QSB_PKG, "card_container")),
+                mABvtHelper.SHORT_TIMEOUT)) != null)) {
+            Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
+        }
+        assertNotNull("Some cards should be loaded", cardContainer);
+    }
+}
diff --git a/tests/androidbvt/src/com/android/androidbvt/LauncherTests.java b/tests/androidbvt/src/com/android/androidbvt/SysUILauncherTests.java
similarity index 99%
rename from tests/androidbvt/src/com/android/androidbvt/LauncherTests.java
rename to tests/androidbvt/src/com/android/androidbvt/SysUILauncherTests.java
index b93ec7f..e209817 100644
--- a/tests/androidbvt/src/com/android/androidbvt/LauncherTests.java
+++ b/tests/androidbvt/src/com/android/androidbvt/SysUILauncherTests.java
@@ -37,7 +37,7 @@
 import java.io.IOException;
 import java.util.List;
 
-public class LauncherTests extends TestCase {
+public class SysUILauncherTests extends TestCase {
     private static final int LONG_TIMEOUT = 5000;
     private static final String APP_NAME = "Clock";
     private static final String PKG_NAME = "com.google.android.deskclock";
diff --git a/tests/androidbvt/src/com/android/androidbvt/SysUILockScreenTests.java b/tests/androidbvt/src/com/android/androidbvt/SysUILockScreenTests.java
index 801f9cd..28192d6 100644
--- a/tests/androidbvt/src/com/android/androidbvt/SysUILockScreenTests.java
+++ b/tests/androidbvt/src/com/android/androidbvt/SysUILockScreenTests.java
@@ -22,22 +22,25 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.Direction;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.Until;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.widget.EditText;
 
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
 public class SysUILockScreenTests extends TestCase {
+    private static final String LAUNCHER_PACKAGE = "com.google.android.googlequicksearchbox";
+    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+    private static final String EDIT_TEXT_CLASS_NAME = "android.widget.EditText";
     private static final int SHORT_TIMEOUT = 200;
     private static final int LONG_TIMEOUT = 2000;
     private static final int PIN = 1234;
     private static final String PASSWORD = "aaaa";
-    private static final String EDIT_TEXT_CLASS_NAME = "android.widget.EditText";
+    private AndroidBvtHelper mABvtHelper = null;
     private UiDevice mDevice = null;
     private Context mContext;
 
@@ -47,6 +50,8 @@
         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         mDevice.freezeRotation();
         mContext = InstrumentationRegistry.getTargetContext();
+        mABvtHelper = AndroidBvtHelper.getInstance(mDevice, mContext,
+                InstrumentationRegistry.getInstrumentation().getUiAutomation());
         mDevice.wakeUp();
         mDevice.pressHome();
     }
@@ -69,7 +74,7 @@
         sleepAndWakeUpDevice();
         unlockScreen(Integer.toString(PIN));
         removeScreenLock(Integer.toString(PIN));
-        Thread.sleep(LONG_TIMEOUT);
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
         Assert.assertFalse("Lock Screen is still enabled", isLockScreenEnabled());
     }
 
@@ -83,7 +88,7 @@
         sleepAndWakeUpDevice();
         unlockScreen(PASSWORD);
         removeScreenLock(PASSWORD);
-        Thread.sleep(LONG_TIMEOUT);
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
         Assert.assertFalse("Lock Screen is still enabled", isLockScreenEnabled());
     }
 
@@ -99,28 +104,46 @@
         checkCheckEmergencyCall();
         unlockScreen(PASSWORD);
         removeScreenLock(PASSWORD);
-        Thread.sleep(LONG_TIMEOUT);
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
         Assert.assertFalse("Lock Screen is still enabled", isLockScreenEnabled());
     }
 
     /**
+     * Just lock the screen and slide up to unlock
+     */
+    @LargeTest
+    public void testSlideUnlock() throws Exception {
+        sleepAndWakeUpDevice();
+        mDevice.wait(Until.findObject(
+                By.res(SYSTEMUI_PACKAGE, "notification_stack_scroller")), 2000)
+                .swipe(Direction.UP, 1.0f);
+        int counter = 6;
+        UiObject2 workspace = mDevice.findObject(By.res(LAUNCHER_PACKAGE, "workspace"));
+        while (counter-- > 0 && workspace == null) {
+            workspace = mDevice.findObject(By.res(LAUNCHER_PACKAGE, "workspace"));
+            Thread.sleep(500);
+        }
+        assertNotNull("Workspace wasn't found", workspace);
+    }
+
+    /**
      * Sets the screen lock pin or password
      * @param pwd text of Password or Pin for lockscreen
      * @param mode indicate if its password or PIN
      */
     private void setScreenLock(String pwd, String mode) throws Exception {
         navigateToScreenLock();
-        mDevice.wait(Until.findObject(By.text(mode)), LONG_TIMEOUT).click();
+        mDevice.wait(Until.findObject(By.text(mode)), mABvtHelper.LONG_TIMEOUT).click();
         // set up Secure start-up page
-        mDevice.wait(Until.findObject(By.text("No thanks")), LONG_TIMEOUT).click();
+        mDevice.wait(Until.findObject(By.text("No thanks")), mABvtHelper.LONG_TIMEOUT).click();
         UiObject2 pinField = mDevice.wait(Until.findObject(By.clazz(EDIT_TEXT_CLASS_NAME)),
-                LONG_TIMEOUT);
+                mABvtHelper.LONG_TIMEOUT);
         pinField.setText(pwd);
         // enter and verify password
         mDevice.pressEnter();
         pinField.setText(pwd);
         mDevice.pressEnter();
-        mDevice.wait(Until.findObject(By.text("DONE")), LONG_TIMEOUT).click();
+        mDevice.wait(Until.findObject(By.text("DONE")), mABvtHelper.LONG_TIMEOUT).click();
     }
 
     /**
@@ -128,57 +151,58 @@
      */
     private void checkCheckEmergencyCall() throws Exception {
         mDevice.pressMenu();
-        mDevice.wait(Until.findObject(By.text("EMERGENCY")), LONG_TIMEOUT).click();
-        Thread.sleep(LONG_TIMEOUT);
-        UiObject2 dialButton = mDevice.wait(Until.findObject(By.desc("dial")), LONG_TIMEOUT);
+        mDevice.wait(Until.findObject(By.text("EMERGENCY")), mABvtHelper.LONG_TIMEOUT).click();
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
+        UiObject2 dialButton = mDevice.wait(Until.findObject(By.desc("dial")),
+                mABvtHelper.LONG_TIMEOUT);
         Assert.assertNotNull("Can't reach emergency call page", dialButton);
         mDevice.pressBack();
-        Thread.sleep(LONG_TIMEOUT);
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
     }
 
     private void removeScreenLock(String pwd) throws Exception {
         navigateToScreenLock();
         UiObject2 pinField = mDevice.wait(Until.findObject(By.clazz(EDIT_TEXT_CLASS_NAME)),
-                LONG_TIMEOUT);
+                mABvtHelper.LONG_TIMEOUT);
         pinField.setText(pwd);
         mDevice.pressEnter();
-        mDevice.wait(Until.findObject(By.text("Swipe")), LONG_TIMEOUT).click();
-        mDevice.wait(Until.findObject(By.text("YES, REMOVE")), LONG_TIMEOUT).click();
+        mDevice.wait(Until.findObject(By.text("Swipe")), mABvtHelper.LONG_TIMEOUT).click();
+        mDevice.wait(Until.findObject(By.text("YES, REMOVE")), mABvtHelper.LONG_TIMEOUT).click();
     }
 
     private void unlockScreen(String pwd) throws Exception {
         swipeUp();
-        Thread.sleep(SHORT_TIMEOUT);
+        Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
         // enter password to unlock screen
         String command = String.format(" %s %s %s", "input", "text", pwd);
         mDevice.executeShellCommand(command);
         mDevice.waitForIdle();
-        Thread.sleep(SHORT_TIMEOUT);
+        Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
         mDevice.pressEnter();
     }
 
     private void navigateToScreenLock() throws Exception {
         launchSettingsPage(mContext, Settings.ACTION_SECURITY_SETTINGS);
-        mDevice.wait(Until.findObject(By.text("Screen lock")), LONG_TIMEOUT).click();
+        mDevice.wait(Until.findObject(By.text("Screen lock")), mABvtHelper.LONG_TIMEOUT).click();
     }
 
     private void launchSettingsPage(Context ctx, String pageName) throws Exception {
         Intent intent = new Intent(pageName);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         ctx.startActivity(intent);
-        Thread.sleep(LONG_TIMEOUT * 2);
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT * 2);
     }
 
     private void sleepAndWakeUpDevice() throws RemoteException, InterruptedException {
         mDevice.sleep();
-        Thread.sleep(LONG_TIMEOUT);
+        Thread.sleep(mABvtHelper.LONG_TIMEOUT);
         mDevice.wakeUp();
     }
 
     private void swipeUp() throws Exception {
         mDevice.swipe(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight(),
                 mDevice.getDisplayWidth() / 2, 0, 30);
-        Thread.sleep(SHORT_TIMEOUT);
+        Thread.sleep(mABvtHelper.SHORT_TIMEOUT);
     }
 
     private boolean isLockScreenEnabled() {
@@ -186,3 +210,4 @@
         return km.isKeyguardSecure();
     }
 }
+
diff --git a/tests/androidbvt/src/com/android/androidbvt/app/MediaPlaybackTestApp.java b/tests/androidbvt/src/com/android/androidbvt/app/MediaPlaybackTestApp.java
new file mode 100644
index 0000000..bfb239b
--- /dev/null
+++ b/tests/androidbvt/src/com/android/androidbvt/app/MediaPlaybackTestApp.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.androidbvt.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import com.android.androidbvt.R;
+
+public class MediaPlaybackTestApp extends Activity {
+
+    private SurfaceView mSurfaceView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.surface_view);
+        mSurfaceView = (SurfaceView)findViewById(R.id.surface_view);
+    }
+
+    public SurfaceHolder getSurfaceHolder() {
+        return mSurfaceView.getHolder();
+    }
+}
diff --git a/tests/perf/PerformanceLaunch/res/values-v11/styles.xml b/tests/perf/PerformanceLaunch/res/values-v11/styles.xml
deleted file mode 100644
index 3c02242..0000000
--- a/tests/perf/PerformanceLaunch/res/values-v11/styles.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<resources>
-
-    <!--
-        Base application theme for API 11+. This theme completely replaces
-        AppBaseTheme from res/values/styles.xml on API 11+ devices.
-    -->
-    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
-        <!-- API 11 theme customizations can go here. -->
-    </style>
-
-</resources>
diff --git a/tests/perf/PerformanceLaunch/res/values-v14/styles.xml b/tests/perf/PerformanceLaunch/res/values-v14/styles.xml
deleted file mode 100644
index a91fd03..0000000
--- a/tests/perf/PerformanceLaunch/res/values-v14/styles.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<resources>
-
-    <!--
-        Base application theme for API 14+. This theme completely replaces
-        AppBaseTheme from BOTH res/values/styles.xml and
-        res/values-v11/styles.xml on API 14+ devices.
-    -->
-    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
-        <!-- API 14 theme customizations can go here. -->
-    </style>
-
-</resources>
diff --git a/tests/perf/PerformanceLaunch/res/values-v21/styles.xml b/tests/perf/PerformanceLaunch/res/values-v21/styles.xml
new file mode 100644
index 0000000..ea65cee
--- /dev/null
+++ b/tests/perf/PerformanceLaunch/res/values-v21/styles.xml
@@ -0,0 +1,4 @@
+<resources>
+    <style name="AppBaseTheme" parent="android:Theme.Material.Light">
+    </style>
+</resources>