Merge "Add projection tests to CtsVerifier Bug: 13598201 Change-Id: I8faf7761efe4dfecb164dbf18c01c7cedabe5d11" into lmp-dev
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index c227665a..4b75be2 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := cts-sensors-tests
 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 3373ef1..5708a50 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -967,6 +967,55 @@
             android:permission="android.permission.BIND_REMOTEVIEWS"
             android:exported="false" />
 
+        <activity android:name=".projection.cube.ProjectionCubeActivity"
+                  android:label="@string/pca_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_projection" />
+        </activity>
+
+        <activity android:name=".projection.widgets.ProjectionWidgetActivity"
+                  android:label="@string/pwa_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_projection" />
+        </activity>
+
+        <activity android:name=".projection.list.ProjectionListActivity"
+                  android:label="@string/pla_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_projection" />
+        </activity>
+
+        <activity android:name=".projection.video.ProjectionVideoActivity"
+                  android:label="@string/pva_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_projection" />
+        </activity>
+
+        <activity android:name=".projection.touch.ProjectionTouchActivity"
+                  android:label="@string/pta_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_projection" />
+        </activity>
+
+        <service android:name=".projection.ProjectionService"
+                 android:label="@string/projection_service_name"
+                 android:process=":projectionservice" />
+
    </application>
 
 </manifest>
diff --git a/apps/CtsVerifier/res/layout/pa_main.xml b/apps/CtsVerifier/res/layout/pa_main.xml
new file mode 100644
index 0000000..b6b8e4b
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pa_main.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <TextureView
+        android:id="@+id/texture_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+     <include layout="@layout/pass_fail_buttons" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/pca_cubes.xml b/apps/CtsVerifier/res/layout/pca_cubes.xml
new file mode 100644
index 0000000..25869b9
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pca_cubes.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true" >
+
+    <android.opengl.GLSurfaceView
+        android:id="@+id/cube_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/pla_list.xml b/apps/CtsVerifier/res/layout/pla_list.xml
new file mode 100644
index 0000000..0973136
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pla_list.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <ListView
+        android:id="@+id/pla_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+    </ListView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/pta_touch.xml b/apps/CtsVerifier/res/layout/pta_touch.xml
new file mode 100644
index 0000000..8cf6e89
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pta_touch.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <com.android.cts.verifier.projection.touch.TouchPointView
+        android:id="@+id/multi_touch_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/pva_video.xml b/apps/CtsVerifier/res/layout/pva_video.xml
new file mode 100644
index 0000000..a62c833
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pva_video.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <VideoView
+        android:id="@+id/video_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/pwa_buttons.xml b/apps/CtsVerifier/res/layout/pwa_buttons.xml
new file mode 100644
index 0000000..fc2d10f
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pwa_buttons.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:gravity="bottom"
+    android:orientation="vertical" >
+
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/pwa_button" />
+
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/pwa_button" />
+
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/pwa_button" />
+
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/pwa_button" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/pwa_widgets.xml b/apps/CtsVerifier/res/layout/pwa_widgets.xml
new file mode 100644
index 0000000..4bfcec6
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pwa_widgets.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+     <TextureView
+         android:id="@+id/texture_view"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent" />
+
+     <LinearLayout
+         android:layout_width="fill_parent"
+         android:layout_height="wrap_content"
+         android:orientation="vertical" >
+
+         <LinearLayout
+             android:layout_width="match_parent"
+             android:layout_height="match_parent" >
+
+             <include layout="@layout/pass_fail_buttons" />
+         </LinearLayout>
+
+         <LinearLayout
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:orientation="horizontal" >
+
+             <Button
+                 android:id="@+id/up_button"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                 android:layout_weight="1"
+                 android:text="@string/pwa_button_up" />
+
+             <Button
+                 android:id="@+id/down_button"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                 android:layout_weight="1"
+                 android:text="@string/pwa_button_down" />
+
+             <Button
+                 android:id="@+id/left_button"
+                 android:layout_width="match_parent"
+                 android:layout_height="match_parent"
+                 android:layout_weight="1"
+                 android:text="@string/pwa_button_left" />
+
+             <Button
+                 android:id="@+id/right_button"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                 android:layout_weight="1"
+                 android:text="@string/pwa_button_right" />
+
+         </LinearLayout>
+     </LinearLayout>
+
+</FrameLayout>
diff --git a/apps/CtsVerifier/res/raw/test_video.mp4 b/apps/CtsVerifier/res/raw/test_video.mp4
new file mode 100644
index 0000000..ab95ac0
--- /dev/null
+++ b/apps/CtsVerifier/res/raw/test_video.mp4
Binary files differ
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index d7e6d33..86729ad 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -961,7 +961,6 @@
         2. Verify that a timer is started  and the timers UI is shown with a timer named "Start Timer Test".\n
         3. Verify that the timer rings after 30 seconds.\n
     </string>
-    
     <!-- Strings for LockConfirmBypassTest -->
     <string name="lock_confirm_test_title">Keyguard Password Verification</string>
     <string name="lock_set_button_text">Set password</string>
@@ -972,4 +971,31 @@
         \nThen click the \"Change password\" button to change it. You should be prompted for the current password first. If you are not, then mark the test as failed.
     </string>
 
+    <!-- String for Projection Tests -->
+    <string name="test_category_projection">Projection Tests</string>
+    <string name="projection_service_name">Projection Service</string>
+    <string name="pca_info">This tests whether or not OpenGL projection works.\n
+        You should see two "tumbling cubes." Tapping the screen should case the cubes to explode.</string>
+    <string name="pca_test">Projection Cube Test</string>
+    <string name="pwa_info">This tests whether or displaying widets and keyfocus navigation works.\n
+        You should see four buttons on the bottom of the screen.\n
+        Pressing the "up" and "down" buttons should highlight different buttons.\n
+        Further, you should also be able to touch them and they should highlight as ususual.</string>
+    <string name="pwa_test">Projection Widget Test</string>
+    <string name="pwa_button_up">Up</string>
+    <string name="pwa_button_down">Down</string>
+    <string name="pwa_button_left">Left</string>
+    <string name="pwa_button_right">Right</string>
+    <string name="pwa_button">Button</string>
+    <string name="pla_test">Projection Scrolling List Test</string>
+    <string name="pla_info">This tests whether a projected list view will scroll properly\n
+        You should see 50 list items and be able to scroll up and down the list</string>
+    <string name="pva_test">Projection Video Playback Test</string>
+    <string name="pva_info">This tests whether video playback works when projected.\n
+        You should see a blinking white box and here a beep that is synchronized with each blink</string>
+    <string name="pta_test">Projection Multitouch Test</string>
+    <string name="pta_info">This tests whether multitouch works.\n
+        Touching the screen should render a dot at the location you touched.\n
+        Touching with additional fingers will render additoinal dots and you should be able to drag them around.</string>
+
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/IProjectionService.aidl b/apps/CtsVerifier/src/com/android/cts/verifier/projection/IProjectionService.aidl
new file mode 100644
index 0000000..0bc1b20
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/IProjectionService.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.projection;
+
+import android.view.Surface;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+interface IProjectionService {
+    void startRendering(in Surface surface, int width, int height, int density, int viewType);
+    void stopRendering();
+    void onTouchEvent(in MotionEvent event);
+    void onKeyEvent(in KeyEvent event);
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectedPresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectedPresentation.java
new file mode 100644
index 0000000..4990ed4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectedPresentation.java
@@ -0,0 +1,51 @@
+/*
+ * 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.projection;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+/**
+ * Base class for Presentations which are to be projected onto a VirtualDisplay
+ */
+public abstract class ProjectedPresentation extends Presentation {
+    public ProjectedPresentation(Context outerContext, Display display) {
+        // This theme is required to prevent an extra view from obscuring the presentation
+        super(outerContext, display, android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor);
+
+        getWindow().setType(WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
+
+        // So we can control the input
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+    }
+
+    public void injectTouchEvent(MotionEvent event) {
+        getWindow().setLocalFocus(true, true);
+        getWindow().injectInputEvent(event);
+    }
+
+    public void injectKeyEvent(KeyEvent event) {
+        getWindow().setLocalFocus(true, false);
+        getWindow().injectInputEvent(event);
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java
new file mode 100644
index 0000000..18d9d43
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java
@@ -0,0 +1,160 @@
+/*
+ * 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.projection;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * Base activity for each projection test case. Handles the service connection and TextureView
+ * listeners
+ */
+public abstract class ProjectionActivity extends PassFailButtons.Activity
+        implements TextureView.SurfaceTextureListener {
+    private static final String TAG = ProjectionActivity.class.getSimpleName();
+    protected Intent mStartIntent;
+    protected TextureView mTextureView;
+    protected volatile Surface mSurface;
+    protected int mWidth;
+    protected int mHeight;
+    protected ProjectionPresentationType mType;
+
+    protected IProjectionService mService;
+    protected final ServiceConnection mConnection = new ServiceConnection() {
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder binder) {
+            mService = IProjectionService.Stub.asInterface(binder);
+            new Handler().post(new Runnable() {
+
+                @Override
+                public void run() {
+                    Log.i(TAG, "onServiceConnected thread " + Thread.currentThread());
+                    DisplayMetrics metrics = ProjectionActivity.this.getResources().getDisplayMetrics();
+                    try {
+                        mService.startRendering(mSurface, mWidth, mHeight, metrics.densityDpi, mType.ordinal());
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Failed to execute startRendering", e);
+                    }
+                }
+
+            });
+
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mService = null;
+        }
+
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        mStartIntent = new Intent(this, ProjectionService.class);
+
+    }
+
+    protected View setContentViewAndInfoResources(int layoutId, int titleId, int infoId) {
+        View view = getLayoutInflater().inflate(layoutId, null);
+        setContentView(view);
+
+        mTextureView = (TextureView) view.findViewById(R.id.texture_view);
+        mTextureView.setSurfaceTextureListener(this);
+        mTextureView.setOnTouchListener(new OnTouchListener() {
+
+            @Override
+            public boolean onTouch(View view, MotionEvent event) {
+                if (mService != null) {
+                    try {
+                        mService.onTouchEvent(event);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Failed to execute onTouchEvent", e);
+                    }
+                }
+                return true;
+            }
+
+        });
+
+        setInfoResources(titleId, infoId, -1);
+        setPassFailButtonClickListeners();
+        return view;
+    }
+
+    @Override
+    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+        Log.i(TAG, "onSurfaceTextureAvailable " + "w: " + width + " h: " + height);
+        mSurface = new Surface(surface);
+        mWidth = width;
+        mHeight = height;
+        if (mService == null) {
+            bindService(mStartIntent, mConnection,
+                    Context.BIND_AUTO_CREATE);
+        }
+
+    }
+
+    @Override
+    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+        Log.i(TAG, "onSurfaceTextureDestroyed");
+        if (mService != null) {
+            try {
+                mService.stopRendering();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to execute stopRendering", e);
+            }
+        }
+        mSurface.release();
+        mSurface = null;
+
+        unbindService(mConnection);
+        return true;
+    }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+        Log.i(TAG, "onSurfaceTextureSizeChanged " + surface.toString() + "w: " + width + " h: "
+                + height);
+    }
+
+    @Override
+    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+        Log.i(TAG, "onSurfaceTextureUpdated " + surface.toString());
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionPresentationType.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionPresentationType.java
new file mode 100644
index 0000000..28f5d46
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionPresentationType.java
@@ -0,0 +1,25 @@
+/*
+ * 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.projection;
+
+public enum ProjectionPresentationType {
+    TUMBLING_CUBES,
+    BASIC_WIDGETS,
+    MULTI_TOUCH,
+    SCROLLING_LIST,
+    VIDEO_PLAYBACK
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionService.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionService.java
new file mode 100644
index 0000000..bfe5a30
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionService.java
@@ -0,0 +1,147 @@
+/*
+ * 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.projection;
+
+import android.app.Service;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+
+import com.android.cts.verifier.projection.cube.CubePresentation;
+import com.android.cts.verifier.projection.list.ListPresentation;
+import com.android.cts.verifier.projection.touch.TouchPresentation;
+import com.android.cts.verifier.projection.video.VideoPresentation;
+import com.android.cts.verifier.projection.widgets.WidgetPresentation;
+
+/**
+ * Service to handle rendering of views on a virtual display and to forward input events to the
+ * display
+ */
+public class ProjectionService extends Service {
+    private final String TAG = ProjectionService.class.getSimpleName();
+    private final String DISPLAY_NAME = "CtsVerifier Virtual Display";
+
+    private Handler mUIHandler;
+
+    private ProjectedPresentation createPresentation(int typeOrdinal) {
+        ProjectionPresentationType type = ProjectionPresentationType.values()[typeOrdinal];
+        switch (type) {
+            case TUMBLING_CUBES:
+                return new CubePresentation(ProjectionService.this, mDisplay.getDisplay());
+
+            case BASIC_WIDGETS:
+                return new WidgetPresentation(ProjectionService.this, mDisplay.getDisplay());
+
+            case SCROLLING_LIST:
+                return new ListPresentation(ProjectionService.this, mDisplay.getDisplay());
+
+            case VIDEO_PLAYBACK:
+                return new VideoPresentation(ProjectionService.this, mDisplay.getDisplay());
+
+            case MULTI_TOUCH:
+                return new TouchPresentation(ProjectionService.this, mDisplay.getDisplay());
+        }
+
+        return null;
+    }
+
+    private class ProjectionServiceBinder extends IProjectionService.Stub {
+        @Override
+        public void startRendering(final Surface surface, final int width, final int height,
+                final int density,
+                final int viewType) throws RemoteException {
+            mUIHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    DisplayManager manager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
+                    Log.i(TAG, "Surface " + surface.toString() + ": "
+                            + Boolean.toString(surface.isValid()));
+                    mDisplay = manager.createVirtualDisplay(DISPLAY_NAME, width, height, density,
+                            surface, 0);
+                    mPresentation = createPresentation(viewType);
+                    if (mPresentation == null) {
+                        return;
+                    }
+
+                    mPresentation.show();
+                }
+            });
+        }
+
+        @Override
+        public void stopRendering() throws RemoteException {
+            mUIHandler.post(new Runnable() {
+
+                @Override
+                public void run() {
+                    if (mPresentation != null) {
+                        mPresentation.dismiss();
+                        mPresentation = null;
+                    }
+                }
+
+            });
+        }
+
+        @Override
+        public void onTouchEvent(final MotionEvent event) throws RemoteException {
+            mUIHandler.post(new Runnable() {
+
+                @Override
+                public void run() {
+                    if (mPresentation != null) {
+                        mPresentation.injectTouchEvent(event);
+                    }
+                }
+
+            });
+        }
+
+        @Override
+        public void onKeyEvent(final KeyEvent event) throws RemoteException {
+            mUIHandler.post(new Runnable() {
+
+                @Override
+                public void run() {
+                    if (mPresentation != null) {
+                        mPresentation.injectKeyEvent(event);
+                    }
+                }
+
+            });
+        }
+    }
+
+    private final IBinder mBinder = new ProjectionServiceBinder();
+    private VirtualDisplay mDisplay;
+    private ProjectedPresentation mPresentation;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.i(TAG, "onBind");
+        mUIHandler = new Handler(Looper.getMainLooper());
+        return mBinder;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/Cube.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/Cube.java
new file mode 100644
index 0000000..0b521fb
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/Cube.java
@@ -0,0 +1,100 @@
+/*
+ * 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.projection.cube;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * A vertex shaded cube.
+ */
+class Cube
+{
+    public Cube()
+    {
+        int one = 0x10000;
+        int vertices[] = {
+                -one, -one, -one,
+                one, -one, -one,
+                one,  one, -one,
+                -one,  one, -one,
+                -one, -one,  one,
+                one, -one,  one,
+                one,  one,  one,
+                -one,  one,  one,
+        };
+
+        int colors[] = {
+                0,    0,    0,  one,
+                one,    0,    0,  one,
+                one,  one,    0,  one,
+                0,  one,    0,  one,
+                0,    0,  one,  one,
+                one,    0,  one,  one,
+                one,  one,  one,  one,
+                0,  one,  one,  one,
+        };
+
+        byte indices[] = {
+                0, 4, 5,    0, 5, 1,
+                1, 5, 6,    1, 6, 2,
+                2, 6, 7,    2, 7, 3,
+                3, 7, 4,    3, 4, 0,
+                4, 7, 6,    4, 6, 5,
+                3, 0, 1,    3, 1, 2
+        };
+
+        // Buffers to be passed to gl*Pointer() functions
+        // must be direct, i.e., they must be placed on the
+        // native heap where the garbage collector cannot
+        // move them.
+        //
+        // Buffers with multi-byte datatypes (e.g., short, int, float)
+        // must have their byte order set to native order
+
+        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
+        vbb.order(ByteOrder.nativeOrder());
+        mVertexBuffer = vbb.asIntBuffer();
+        mVertexBuffer.put(vertices);
+        mVertexBuffer.position(0);
+
+        ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
+        cbb.order(ByteOrder.nativeOrder());
+        mColorBuffer = cbb.asIntBuffer();
+        mColorBuffer.put(colors);
+        mColorBuffer.position(0);
+
+        mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
+        mIndexBuffer.put(indices);
+        mIndexBuffer.position(0);
+    }
+
+    public void draw(GL10 gl)
+    {
+        gl.glFrontFace(GL10.GL_CW);
+        gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);
+        gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer);
+        gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
+    }
+
+    private IntBuffer   mVertexBuffer;
+    private IntBuffer   mColorBuffer;
+    private ByteBuffer  mIndexBuffer;
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubePresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubePresentation.java
new file mode 100644
index 0000000..bf7825b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubePresentation.java
@@ -0,0 +1,60 @@
+/*
+ * 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.projection.cube;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectedPresentation;
+
+
+
+/**
+ * Render tumbling cubes
+ *
+ */
+public class CubePresentation extends ProjectedPresentation {
+    public CubePresentation(Context context, Display display) {
+        super(context, display);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        View view = getLayoutInflater().inflate(R.layout.pca_cubes, null);
+        setContentView(view);
+
+        GLSurfaceView cubeView = (GLSurfaceView) view.findViewById(R.id.cube_view);
+        final CubeRenderer renderer = new CubeRenderer(true);
+        cubeView.setRenderer(renderer);
+
+        cubeView.setOnTouchListener(new OnTouchListener() {
+
+            @Override
+            public boolean onTouch(View view, MotionEvent event) {
+                renderer.explode();
+                return true;
+            }
+        });
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubeRenderer.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubeRenderer.java
new file mode 100644
index 0000000..9205fec
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubeRenderer.java
@@ -0,0 +1,128 @@
+/*
+ * 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.projection.cube;
+
+import android.opengl.GLSurfaceView;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Render a pair of tumbling cubes.
+ */
+public class CubeRenderer implements GLSurfaceView.Renderer {
+
+    private final boolean mTranslucentBackground;
+
+    private final Cube mCube;
+    private float mAngle;
+    private float mScale = 1.0f;
+    private boolean mExploding;
+
+    public CubeRenderer(boolean useTranslucentBackground) {
+        mTranslucentBackground = useTranslucentBackground;
+        mCube = new Cube();
+    }
+
+    public void explode() {
+        mExploding = true;
+    }
+
+    @Override
+    public void onDrawFrame(GL10 gl) {
+        /*
+         * Usually, the first thing one might want to do is to clear
+         * the screen. The most efficient way of doing this is to use
+         * glClear().
+         */
+
+        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+        /*
+         * Now we're ready to draw some 3D objects
+         */
+
+        gl.glMatrixMode(GL10.GL_MODELVIEW);
+        gl.glLoadIdentity();
+        gl.glTranslatef(0, 0, -3.0f);
+        gl.glRotatef(mAngle,        0, 1, 0);
+        gl.glRotatef(mAngle*0.25f,  1, 0, 0);
+
+        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
+
+        gl.glScalef(mScale, mScale, mScale);
+        mCube.draw(gl);
+
+        gl.glRotatef(mAngle*2.0f, 0, 1, 1);
+        gl.glTranslatef(0.5f, 0.5f, 0.5f);
+
+        mCube.draw(gl);
+
+        mAngle += 1.2f;
+
+        if (mExploding) {
+            mScale *= 1.02f;
+            if (mScale > 4.0f) {
+                mScale = 1.0f;
+                mExploding = false;
+            }
+        }
+    }
+
+    @Override
+    public void onSurfaceChanged(GL10 gl, int width, int height) {
+        gl.glViewport(0, 0, width, height);
+
+        /*
+         * Set our projection matrix. This doesn't have to be done
+         * each time we draw, but usually a new projection needs to
+         * be set when the viewport is resized.
+         */
+
+        float ratio = (float) width / height;
+        gl.glMatrixMode(GL10.GL_PROJECTION);
+        gl.glLoadIdentity();
+        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+    }
+
+    @Override
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        /*
+         * By default, OpenGL enables features that improve quality
+         * but reduce performance. One might want to tweak that
+         * especially on software renderer.
+         */
+        gl.glDisable(GL10.GL_DITHER);
+
+        /*
+         * Some one-time OpenGL initialization can be made here
+         * probably based on features of this particular context
+         */
+        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
+                GL10.GL_FASTEST);
+
+        if (mTranslucentBackground) {
+            gl.glClearColor(0,0,0,0);
+        } else {
+            gl.glClearColor(1,1,1,1);
+        }
+        gl.glEnable(GL10.GL_CULL_FACE);
+        gl.glShadeModel(GL10.GL_SMOOTH);
+        gl.glEnable(GL10.GL_DEPTH_TEST);
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/ProjectionCubeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/ProjectionCubeActivity.java
new file mode 100644
index 0000000..0ef9a30
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/ProjectionCubeActivity.java
@@ -0,0 +1,37 @@
+/*
+ * 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.projection.cube;
+
+import com.android.cts.verifier.R;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+
+public class ProjectionCubeActivity extends ProjectionActivity {
+    private static final String TAG = ProjectionCubeActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        setContentViewAndInfoResources(R.layout.pa_main, R.string.pca_test, R.string.pca_info);
+        mType = ProjectionPresentationType.TUMBLING_CUBES;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ListPresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ListPresentation.java
new file mode 100644
index 0000000..dad4945
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ListPresentation.java
@@ -0,0 +1,62 @@
+/*
+ * 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.projection.list;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectedPresentation;
+
+import java.util.ArrayList;
+
+/**
+ * Render a list view that scrolls
+ */
+public class ListPresentation extends ProjectedPresentation {
+    private ArrayList<String> mItemList = new ArrayList<String>();
+    private static final int NUM_ITEMS = 50; // Enough to make the list scroll
+
+    /**
+     * @param outerContext
+     * @param display
+     */
+    public ListPresentation(Context outerContext, Display display) {
+        super(outerContext, display);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        View view = getLayoutInflater().inflate(R.layout.pla_list, null);
+        setContentView(view);
+
+        for (int i = 0; i < NUM_ITEMS; ++i) {
+            mItemList.add("Item #" + i);
+        }
+
+        ListView listView = (ListView) view.findViewById(R.id.pla_list);
+
+        listView.setAdapter(new ArrayAdapter<String>(getContext(),
+                android.R.layout.simple_list_item_1, mItemList));
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ProjectionListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ProjectionListActivity.java
new file mode 100644
index 0000000..c166320
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ProjectionListActivity.java
@@ -0,0 +1,37 @@
+/*
+ * 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.projection.list;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+import com.android.cts.verifier.projection.cube.ProjectionCubeActivity;
+
+public class ProjectionListActivity extends ProjectionActivity {
+    private static final String TAG = ProjectionCubeActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        setContentViewAndInfoResources(R.layout.pa_main, R.string.pla_test, R.string.pla_info);
+        mType = ProjectionPresentationType.SCROLLING_LIST;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/ProjectionTouchActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/ProjectionTouchActivity.java
new file mode 100644
index 0000000..ed1d881
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/ProjectionTouchActivity.java
@@ -0,0 +1,37 @@
+/*
+ * 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.projection.touch;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+import com.android.cts.verifier.projection.cube.ProjectionCubeActivity;
+
+public class ProjectionTouchActivity extends ProjectionActivity {
+    private static final String TAG = ProjectionCubeActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        setContentViewAndInfoResources(R.layout.pa_main, R.string.pta_test, R.string.pta_info);
+        mType = ProjectionPresentationType.MULTI_TOUCH;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/TouchPointView.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/TouchPointView.java
new file mode 100644
index 0000000..c88fd79
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/TouchPointView.java
@@ -0,0 +1,98 @@
+/*
+ * 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.projection.touch;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+/*
+ * Simple view that draws a circle where a touch is registered
+ */
+public class TouchPointView extends View {
+    @SuppressWarnings("unused")
+    private static final String TAG = TouchPointView.class.getSimpleName();
+
+    private final int[] mColors = {
+            Color.RED,
+            Color.GREEN,
+            Color.BLUE,
+            Color.YELLOW,
+            Color.MAGENTA,
+            Color.BLACK,
+            Color.DKGRAY
+    };
+    List<Finger> mFingers;
+    Paint mPaint;
+
+    public TouchPointView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TouchPointView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mFingers = new ArrayList<Finger>();
+
+        mPaint = new Paint();
+        mPaint.setStyle(Paint.Style.FILL);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        mFingers.clear();
+        if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+            invalidate();
+            return true;
+        }
+        for (int i = 0; i < event.getPointerCount(); i++) {
+            int pointerId = event.getPointerId(i);
+            int pointerIndex = event.findPointerIndex(pointerId);
+            Finger finger = new Finger();
+            finger.point =  new Point((int)event.getX(pointerIndex), (int)event.getY(pointerIndex));
+            finger.pointerId = pointerId;
+
+            mFingers.add(finger);
+        }
+        invalidate();
+        return true;
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        int radius = canvas.getWidth() / 20;
+        for (int i = 0; i < mFingers.size(); i++) {
+            Finger finger = mFingers.get(i);
+            Point point = finger.point;
+            int color = mColors[finger.pointerId % mColors.length];
+            mPaint.setColor(color);
+            canvas.drawCircle(point.x, point.y, radius, mPaint);
+        }
+    }
+
+    private class Finger {
+        public Point point;
+        public int pointerId;
+    }
+}
+
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/ProjectionVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/ProjectionVideoActivity.java
new file mode 100644
index 0000000..7a32b27
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/ProjectionVideoActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.projection.video;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+
+public class ProjectionVideoActivity extends ProjectionActivity {
+    private static final String TAG = ProjectionVideoActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        setContentViewAndInfoResources(R.layout.pa_main, R.string.pva_test, R.string.pva_info);
+        mType = ProjectionPresentationType.VIDEO_PLAYBACK;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/VideoPresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/VideoPresentation.java
new file mode 100644
index 0000000..4275cb8
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/VideoPresentation.java
@@ -0,0 +1,60 @@
+/*
+ * 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.projection.video;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.View;
+import android.widget.VideoView;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectedPresentation;
+
+/**
+ * Play a test video that determines if the video and audio are in sync in projected presentations
+ */
+public class VideoPresentation extends ProjectedPresentation {
+
+    /**
+     * @param outerContext
+     * @param display
+     */
+    public VideoPresentation(Context outerContext, Display display) {
+        super(outerContext, display);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        View view = getLayoutInflater().inflate(R.layout.pva_video, null);
+        setContentView(view);
+        VideoView videoView = (VideoView) view.findViewById(R.id.video_view);
+        videoView.setOnPreparedListener(new OnPreparedListener() {
+            @Override
+            public void onPrepared(MediaPlayer mp) {
+                mp.setLooping(true);
+            }
+        });
+        String packageName = getContext().getPackageName();
+        Uri uri = Uri.parse("android.resource://" + packageName + "/" + R.raw.test_video);
+        videoView.setVideoURI(uri);
+        videoView.start();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
new file mode 100644
index 0000000..9b862de
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
@@ -0,0 +1,79 @@
+/*
+ * 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.projection.widgets;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+
+public class ProjectionWidgetActivity extends ProjectionActivity {
+    private static final String TAG = ProjectionWidgetActivity.class.getSimpleName();
+
+    private class InjectDPadClickListener implements OnClickListener {
+        private int mKeyCode;
+
+        InjectDPadClickListener(int keyCode) {
+            mKeyCode = keyCode;
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (mService != null) {
+                try {
+                    mService.onKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, mKeyCode));
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error executing onKeyEvent", e);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        View view = setContentViewAndInfoResources(R.layout.pwa_widgets, R.string.pwa_test,
+                R.string.pwa_info);
+
+        mType = ProjectionPresentationType.BASIC_WIDGETS;
+
+        Button button;
+        {
+            button = (Button) view.findViewById(R.id.up_button);
+            button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_UP));
+        }
+        {
+            button = (Button) view.findViewById(R.id.down_button);
+            button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_DOWN));
+        }
+        {
+            button = (Button) view.findViewById(R.id.right_button);
+            button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_RIGHT));
+        }
+        {
+            button = (Button) view.findViewById(R.id.left_button);
+            button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_LEFT));
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/WidgetPresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/WidgetPresentation.java
new file mode 100644
index 0000000..8af2757
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/WidgetPresentation.java
@@ -0,0 +1,46 @@
+/*
+ * 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.projection.widgets;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Display;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectedPresentation;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+
+/**
+ * Check if widgets display and that key focus works in projected mode
+ */
+public class WidgetPresentation extends ProjectedPresentation {
+
+    /**
+     * @param outerContext
+     * @param display
+     */
+    public WidgetPresentation(Context outerContext, Display display) {
+        super(outerContext, display);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pwa_buttons);
+    }
+
+}