TV: add multiple tracks tests to CtsVerifier DO NOT MERGE

Bug: 18069561
Change-Id: I9a8dbb7b991a3cbbcf53be01cd17b9bb5aa4591f
(cherry picked from commit f570731f846a88a26766115fa78a35fdbe24f82a)
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 098cdad..563a65e 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1483,6 +1483,17 @@
                     android:value="android.software.live_tv" />
         </activity>
 
+        <activity android:name=".tv.MultipleTracksTestActivity"
+                android:label="@string/tv_multiple_tracks_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_tv" />
+            <meta-data android:name="test_required_features"
+                    android:value="android.software.live_tv" />
+        </activity>
+
         <activity android:name=".tv.MockTvInputSettingsActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 83af341..e8cc499 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1387,6 +1387,29 @@
     CtsVerifier channel is not set up. Please set up before proceeding.
     </string>
 
+    <string name="tv_multiple_tracks_test">Live Channels app multiple tracks / subtitle test</string>
+    <string name="tv_multiple_tracks_test_info">
+    This test verifies that the default Live Channels app invokes proper mulitple tracks / subtitle
+    APIs in the framework.
+    </string>
+    <string name="tv_multiple_tracks_test_select_subtitle">
+    Press the \"Launch Live Channels\" button. Verify that the closed caption is off by default.
+    Set closed caption to English.
+    </string>
+    <string name="tv_multiple_tracks_test_verify_set_caption_enabled">
+    Caption should be enabled.
+    </string>
+    <string name="tv_multiple_tracks_test_verify_select_subtitle">
+    The English subtitle track should be selected.
+    </string>
+    <string name="tv_multiple_tracks_test_select_audio">
+    Press the \"Launch Live Channels\" button. Verify that the audio track is English by default.
+    Select Spanish audio track.
+    </string>
+    <string name="tv_multiple_tracks_test_verify_select_audio">
+    The Spanish audio track should be selected.
+    </string>
+
     <string name="overlay_view_text">Overlay View Dummy Text</string>
     <string name="fake_rating">Fake</string>
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
index a6bb01a..f4460de 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
@@ -30,7 +30,9 @@
 import android.media.tv.TvContentRating;
 import android.media.tv.TvInputManager;
 import android.media.tv.TvInputService;
+import android.media.tv.TvTrackInfo;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.view.Surface;
@@ -40,26 +42,57 @@
 
 import com.android.cts.verifier.R;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class MockTvInputService extends TvInputService {
     private static final String TAG = "MockTvInputService";
 
+    private static final String BROADCAST_ACTION = "action";
+    private static final String SELECT_TRACK_TYPE = "type";
+    private static final String SELECT_TRACK_ID = "id";
+    private static final String CAPTION_ENABLED = "enabled";
+
     private static Object sLock = new Object();
     private static Callback sTuneCallback = null;
     private static Callback sOverlayViewCallback = null;
     private static Callback sBroadcastCallback = null;
-    private static String sExpectedBroadcastAction = null;
     private static Callback sUnblockContentCallback = null;
+    private static Callback sSelectTrackCallback = null;
+    private static Callback sSetCaptionEnabledCallback = null;
     private static TvContentRating sRating = null;
 
+    static final TvTrackInfo sEngAudioTrack =
+            new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio_eng")
+            .setAudioChannelCount(2)
+            .setAudioSampleRate(48000)
+            .setLanguage("eng")
+            .build();
+    static final TvTrackInfo sSpaAudioTrack =
+            new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio_spa")
+            .setAudioChannelCount(2)
+            .setAudioSampleRate(48000)
+            .setLanguage("spa")
+            .build();
+    static final TvTrackInfo sEngSubtitleTrack =
+            new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle_eng")
+            .setLanguage("eng")
+            .build();
+    static final TvTrackInfo sSpaSubtitleTrack =
+            new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle_spa")
+            .setLanguage("spa")
+            .build();
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             synchronized (sLock) {
                 if (sBroadcastCallback != null) {
-                    if (intent.getAction().equals(sExpectedBroadcastAction)) {
+                    String expectedAction =
+                            sBroadcastCallback.getBundle().getString(BROADCAST_ACTION);
+                    if (intent.getAction().equals(expectedAction)) {
                         sBroadcastCallback.post();
                         sBroadcastCallback = null;
-                        sExpectedBroadcastAction = null;
                     }
                 }
             }
@@ -75,7 +108,7 @@
     static void expectBroadcast(View postTarget, String action, Runnable successCallback) {
         synchronized (sLock) {
             sBroadcastCallback = new Callback(postTarget, successCallback);
-            sExpectedBroadcastAction = action;
+            sBroadcastCallback.getBundle().putString(BROADCAST_ACTION, action);
         }
     }
 
@@ -97,6 +130,22 @@
         }
     }
 
+    static void expectSelectTrack(int type, String id, View postTarget, Runnable successCallback) {
+        synchronized (sLock) {
+            sSelectTrackCallback = new Callback(postTarget, successCallback);
+            sSelectTrackCallback.getBundle().putInt(SELECT_TRACK_TYPE, type);
+            sSelectTrackCallback.getBundle().putString(SELECT_TRACK_ID, id);
+        }
+    }
+
+    static void expectSetCaptionEnabled(boolean enabled, View postTarget,
+            Runnable successCallback) {
+        synchronized (sLock) {
+            sSetCaptionEnabledCallback = new Callback(postTarget, successCallback);
+            sSetCaptionEnabledCallback.getBundle().putBoolean(CAPTION_ENABLED, enabled);
+        }
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -122,10 +171,15 @@
     private static class MockSessionImpl extends Session {
         private final Context mContext;
         private Surface mSurface = null;
+        private List<TvTrackInfo> mTracks = new ArrayList<>();
 
         private MockSessionImpl(Context context) {
             super(context);
             mContext = context;
+            mTracks.add(sEngAudioTrack);
+            mTracks.add(sSpaAudioTrack);
+            mTracks.add(sEngSubtitleTrack);
+            mTracks.add(sSpaSubtitleTrack);
         }
 
         @Override
@@ -203,16 +257,39 @@
                 }
             }
             notifyVideoAvailable();
+            notifyTracksChanged(mTracks);
+            notifyTrackSelected(TvTrackInfo.TYPE_AUDIO, sEngAudioTrack.getId());
+            notifyTrackSelected(TvTrackInfo.TYPE_SUBTITLE, null);
             return true;
         }
 
         @Override
         public boolean onSelectTrack(int type, String trackId) {
+            synchronized (sLock) {
+                if (sSelectTrackCallback != null) {
+                    Bundle bundle = sSelectTrackCallback.getBundle();
+                    if (bundle.getInt(SELECT_TRACK_TYPE) == type
+                            && bundle.getString(SELECT_TRACK_ID).equals(trackId)) {
+                        sSelectTrackCallback.post();
+                        sSelectTrackCallback = null;
+                    }
+                }
+            }
+            notifyTrackSelected(type, trackId);
             return true;
         }
 
         @Override
         public void onSetCaptionEnabled(boolean enabled) {
+            synchronized (sLock) {
+                if (sSetCaptionEnabledCallback != null) {
+                    Bundle bundle = sSetCaptionEnabledCallback.getBundle();
+                    if (bundle.getBoolean(CAPTION_ENABLED) == enabled) {
+                        sSetCaptionEnabledCallback.post();
+                        sSetCaptionEnabledCallback = null;
+                    }
+                }
+            }
         }
 
         @Override
@@ -230,6 +307,7 @@
     private static class Callback {
         private final View mPostTarget;
         private final Runnable mAction;
+        private final Bundle mBundle = new Bundle();
 
         Callback(View postTarget, Runnable action) {
             mPostTarget = postTarget;
@@ -239,5 +317,9 @@
         public void post() {
             mPostTarget.post(mAction);
         }
+
+        public Bundle getBundle() {
+            return mBundle;
+        }
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java
new file mode 100644
index 0000000..66af4c6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014 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 com.android.cts.verifier.R;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvTrackInfo;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Tests for verifying TV app behavior on multiple tracks and subtitle.
+ */
+public class MultipleTracksTestActivity extends TvAppVerifierActivity
+        implements View.OnClickListener {
+    private static final String TAG = "MultipleTracksTestActivity";
+
+    private static final long TIMEOUT_MS = 5l * 60l * 1000l;  // 5 mins.
+
+    private View mSelectSubtitleItem;
+    private View mVerifySetCaptionEnabledItem;
+    private View mVerifySelectSubtitleItem;
+    private View mSelectAudioItem;
+    private View mVerifySelectAudioItem;
+
+    private Intent mTvAppIntent = null;
+
+    @Override
+    public void onClick(View v) {
+        final View postTarget = getPostTarget();
+
+        if (containsButton(mSelectSubtitleItem, v)) {
+            final Runnable failCallback = new Runnable() {
+                @Override
+                public void run() {
+                    setPassState(mVerifySetCaptionEnabledItem, false);
+                    setPassState(mVerifySelectSubtitleItem, false);
+                }
+            };
+            postTarget.postDelayed(failCallback, TIMEOUT_MS);
+            MockTvInputService.expectSetCaptionEnabled(true, postTarget, new Runnable() {
+                @Override
+                public void run() {
+                    postTarget.removeCallbacks(failCallback);
+                    setPassState(mSelectSubtitleItem, true);
+                    setPassState(mVerifySetCaptionEnabledItem, true);
+                    Integer tag = (Integer) mSelectAudioItem.getTag();
+                    if (tag == 0) {
+                        mSelectAudioItem.setTag(Integer.valueOf(1));
+                    } else if (tag == 1) {
+                        setButtonEnabled(mSelectAudioItem, true);
+                    }
+                }
+            });
+            MockTvInputService.expectSelectTrack(TvTrackInfo.TYPE_SUBTITLE,
+                    MockTvInputService.sEngSubtitleTrack.getId(), postTarget, new Runnable() {
+                @Override
+                public void run() {
+                    postTarget.removeCallbacks(failCallback);
+                    setPassState(mSelectSubtitleItem, true);
+                    setPassState(mVerifySelectSubtitleItem, true);
+                    Integer tag = (Integer) mSelectAudioItem.getTag();
+                    if (tag == 0) {
+                        mSelectAudioItem.setTag(Integer.valueOf(1));
+                    } else if (tag == 1) {
+                        setButtonEnabled(mSelectAudioItem, true);
+                    }
+                }
+            });
+        } else if (containsButton(mSelectAudioItem, v)) {
+            final Runnable failCallback = new Runnable() {
+                @Override
+                public void run() {
+                    setPassState(mVerifySelectAudioItem, false);
+                }
+            };
+            postTarget.postDelayed(failCallback, TIMEOUT_MS);
+            MockTvInputService.expectSelectTrack(TvTrackInfo.TYPE_AUDIO,
+                    MockTvInputService.sSpaAudioTrack.getId(), postTarget, new Runnable() {
+                @Override
+                public void run() {
+                    postTarget.removeCallbacks(failCallback);
+                    setPassState(mSelectAudioItem, true);
+                    setPassState(mVerifySelectAudioItem, true);
+                    getPassButton().setEnabled(true);
+                }
+            });
+        }
+        if (mTvAppIntent == null) {
+            String[] projection = { TvContract.Channels._ID };
+            try (Cursor cursor = getContentResolver().query(TvContract.Channels.CONTENT_URI,
+                    projection, null, null, null)) {
+                if (cursor != null && cursor.moveToNext()) {
+                    mTvAppIntent = new Intent(Intent.ACTION_VIEW,
+                            TvContract.buildChannelUri(cursor.getLong(0)));
+                }
+            }
+            if (mTvAppIntent == null) {
+                Toast.makeText(this, R.string.tv_channel_not_found, Toast.LENGTH_SHORT).show();
+                return;
+            }
+        }
+        startActivity(mTvAppIntent);
+    }
+
+    @Override
+    protected void createTestItems() {
+        mSelectSubtitleItem = createUserItem(
+                R.string.tv_multiple_tracks_test_select_subtitle,
+                R.string.tv_launch_tv_app, this);
+        setButtonEnabled(mSelectSubtitleItem, true);
+        mVerifySetCaptionEnabledItem = createAutoItem(
+                R.string.tv_multiple_tracks_test_verify_set_caption_enabled);
+        mVerifySelectSubtitleItem = createAutoItem(
+                R.string.tv_multiple_tracks_test_verify_select_subtitle);
+        mSelectAudioItem = createUserItem(
+                R.string.tv_multiple_tracks_test_select_audio,
+                R.string.tv_launch_tv_app, this);
+        mSelectAudioItem.setTag(Integer.valueOf(0));
+        mVerifySelectAudioItem = createAutoItem(
+                R.string.tv_multiple_tracks_test_verify_select_audio);
+    }
+
+    @Override
+    protected void setInfoResources() {
+        setInfoResources(R.string.tv_multiple_tracks_test,
+                R.string.tv_multiple_tracks_test_info, -1);
+    }
+}