Merge "Add option to set preview size per camera explicitly." into jb-mr2-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 9ff7edc..abab023 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -21,7 +21,7 @@
       android:versionName="4.3_r1">
 
     <!-- Using 10 for more complete NFC support... -->
-    <uses-sdk android:minSdkVersion="10"></uses-sdk>
+    <uses-sdk android:minSdkVersion="11"></uses-sdk>
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -388,7 +388,27 @@
             <meta-data android:name="test_category" android:value="@string/test_category_networking" />
             <meta-data android:name="test_required_features" android:value="android.hardware.wifi.direct" />
         </activity>
+        
+        <activity android:name=".nls.NotificationListenerVerifierActivity"
+                android:label="@string/nls_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_notifications" />
+        </activity>
 
+        <service android:name=".nls.MockListener"
+                 android:exported="true"
+                 android:label="@string/nls_service_name"
+                 android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationListenerService" />
+            </intent-filter>
+        </service>
+        
+        <service  android:name="nls.NotificationListenerVerifierActivity$DismissService"/>
+        
         <activity android:name=".p2p.GoNegRequesterTestListActivity"
                 android:label="@string/p2p_go_neg_requester"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
diff --git a/apps/CtsVerifier/res/layout/nls_item.xml b/apps/CtsVerifier/res/layout/nls_item.xml
new file mode 100644
index 0000000..3ced3cc
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/nls_item.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 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="wrap_content" >
+
+    <ImageView
+        android:id="@+id/nls_status"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:layout_marginTop="10dip"
+        android:contentDescription="@string/pass_button_text"
+        android:padding="10dip"
+        android:src="@drawable/fs_indeterminate" />
+
+    <TextView
+        android:id="@+id/nls_instructions"
+        style="@style/InstructionsSmallFont"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentTop="true"
+        android:layout_toRightOf="@id/nls_status"
+        android:text="@string/nls_enable_service" />
+
+    <Button
+        android:id="@+id/nls_launch_settings"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_below="@id/nls_instructions"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:layout_toRightOf="@id/nls_status"
+        android:onClick="launchSettings"
+        android:text="@string/nls_start_settings" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/nls_main.xml b/apps/CtsVerifier/res/layout/nls_main.xml
new file mode 100644
index 0000000..0ee4cbf
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/nls_main.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 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"
+    android:padding="10dip" >
+
+    <ScrollView
+        android:id="@+id/nls_test_scroller"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:padding="10dip" >
+
+        <LinearLayout
+            android:id="@+id/nls_test_items"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical" >
+        </LinearLayout>
+    </ScrollView>
+
+    <include
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 280fbec..18a681a 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -581,4 +581,24 @@
     <string name="camera_fov_enable_compass_mode_description">If enabled, the panorama orients
         itself according to the current rotation of the device.</string>
 
+    <string name="test_category_notifications">Notifications</string>
+    <string name="nls_test">Notification Listener Test</string>
+    <string name="nls_service_name">Notification Listener for CTS Verifier</string>
+    <string name="nls_info">This test checks that a NotificationListenerService can be enabled
+        and disabled, and that once enabled the service is able to receive notificaitons and
+        dismiss them.
+    </string>
+    <string name="nls_enable_service">Please enable \"Notification Listener for CTS Verifier\" 
+        under Security > Notification Access and return here.</string>
+    <string name="nls_disable_service">Please disable \"Notification Listener for CTS Verifier\" 
+        under Security > Notification Access and return here.</string>
+    <string name="nls_start_settings">Launch Settings</string>
+    <string name="nls_service_started">Service should start once enabled.</string>
+    <string name="nls_note_received">Check that notification was received.</string>
+    <string name="nls_payload_intact">Check that notification payload was intact.</string>
+    <string name="nls_clear_one">Check that service can clear a notification.</string>
+    <string name="nls_clear_all">Check that service can clear all notifications.</string>
+    <string name="nls_service_stopped">Service should stop once disabled.</string>
+    <string name="nls_note_missed">Check that notification was not received.</string>
+    
 </resources>
diff --git a/apps/CtsVerifier/res/values/styles.xml b/apps/CtsVerifier/res/values/styles.xml
index 0dfd02d..16fa87f 100644
--- a/apps/CtsVerifier/res/values/styles.xml
+++ b/apps/CtsVerifier/res/values/styles.xml
@@ -3,6 +3,8 @@
     <style name="InstructionsFont" parent="@android:style/TextAppearance.Large">
         <item name="android:padding">10dp</item>
     </style>
+    <style name="InstructionsSmallFont" parent="@android:style/TextAppearance.Medium">
+    </style>
     <style name="MessageRow">
         <item name="android:gravity">center_vertical</item>
         <item name="android:textSize">18sp</item>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nls/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/nls/MockListener.java
new file mode 100644
index 0000000..62a09c9
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nls/MockListener.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2013 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.nls;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MockListener extends NotificationListenerService {
+    static final String TAG = "MockListener";
+
+    static final String SERVICE_CHECK = "android.service.notification.cts.SERVICE_CHECK";
+    static final String SERVICE_POSTED = "android.service.notification.cts.SERVICE_POSTED";
+    static final String SERVICE_PAYLOADS = "android.service.notification.cts.SERVICE_PAYLOADS";
+    static final String SERVICE_REMOVED = "android.service.notification.cts.SERVICE_REMOVED";
+    static final String SERVICE_RESET = "android.service.notification.cts.SERVICE_RESET";
+    static final String SERVICE_CLEAR_ONE = "android.service.notification.cts.SERVICE_CLEAR_ONE";
+    static final String SERVICE_CLEAR_ALL = "android.service.notification.cts.SERVICE_CLEAR_ALL";
+
+    static final String EXTRA_PAYLOAD = "TAGS";
+    static final String EXTRA_TAG = "TAG";
+    static final String EXTRA_CODE = "CODE";
+
+    static final int RESULT_TIMEOUT = Activity.RESULT_FIRST_USER;
+    static final int RESULT_NO_SERVER = Activity.RESULT_FIRST_USER + 1;
+
+    private ArrayList<String> mPosted = new ArrayList<String>();
+    private ArrayList<String> mPayloads = new ArrayList<String>();
+    private ArrayList<String> mRemoved = new ArrayList<String>();
+    private BroadcastReceiver mReceiver;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.d(TAG, "created");
+
+        mPosted = new ArrayList<String>();
+        mRemoved = new ArrayList<String>();
+
+        mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (SERVICE_CHECK.equals(action)) {
+                    Log.d(TAG, "SERVICE_CHECK");
+                    setResultCode(Activity.RESULT_OK);
+                } else if (SERVICE_POSTED.equals(action)) {
+                    Log.d(TAG, "SERVICE_POSTED");
+                    Bundle bundle = new Bundle();
+                    bundle.putStringArrayList(EXTRA_PAYLOAD, mPosted);
+                    setResultExtras(bundle);
+                    setResultCode(Activity.RESULT_OK);
+                } else if (SERVICE_PAYLOADS.equals(action)) {
+                    Log.d(TAG, "SERVICE_PAYLOADS");
+                    Bundle bundle = new Bundle();
+                    bundle.putStringArrayList(EXTRA_PAYLOAD, mPayloads);
+                    setResultExtras(bundle);
+                    setResultCode(Activity.RESULT_OK);
+                } else if (SERVICE_REMOVED.equals(action)) {
+                    Log.d(TAG, "SERVICE_REMOVED");
+                    Bundle bundle = new Bundle();
+                    bundle.putStringArrayList(EXTRA_PAYLOAD, mRemoved);
+                    setResultExtras(bundle);
+                    setResultCode(Activity.RESULT_OK);
+                } else if (SERVICE_CLEAR_ONE.equals(action)) {
+                    Log.d(TAG, "SERVICE_CLEAR_ONE");
+                    MockListener.this.cancelNotification(
+                            context.getApplicationInfo().packageName,
+                            intent.getStringExtra(EXTRA_TAG),
+                            intent.getIntExtra(EXTRA_CODE, 0));
+                } else if (SERVICE_CLEAR_ALL.equals(action)) {
+                    Log.d(TAG, "SERVICE_CLEAR_ALL");
+                    MockListener.this.cancelAllNotifications();
+                } else if (SERVICE_RESET.equals(action)) {
+                    Log.d(TAG, "SERVICE_RESET");
+                    resetData();
+                } else {
+                    Log.w(TAG, "unknown action");
+                    setResultCode(Activity.RESULT_CANCELED);
+                }
+            }
+        };
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(SERVICE_CHECK);
+        filter.addAction(SERVICE_POSTED);
+        filter.addAction(SERVICE_PAYLOADS);
+        filter.addAction(SERVICE_REMOVED);
+        filter.addAction(SERVICE_CLEAR_ONE);
+        filter.addAction(SERVICE_CLEAR_ALL);
+        filter.addAction(SERVICE_RESET);
+        registerReceiver(mReceiver, filter);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+        mReceiver = null;
+        Log.d(TAG, "destroyed");
+    }
+
+    public void resetData() {
+        mPosted.clear();
+        mPayloads.clear();
+        mRemoved.clear();
+        Log.d(TAG, "reset");
+    }
+
+    @Override
+    public void onNotificationPosted(StatusBarNotification sbn) {
+        Log.d(TAG, "posted: " + sbn.getTag());
+        mPosted.add(sbn.getTag());
+        StringBuilder payload = new StringBuilder();
+        payload.append(sbn.getTag());
+        payload.append(":");
+        payload.append(sbn.getId());
+        payload.append(":");
+        payload.append(sbn.getPackageName());
+        mPayloads.add(payload.toString());
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn) {
+        Log.d(TAG, "removed: " + sbn.getTag());
+        mRemoved.add(sbn.getTag());
+    }
+
+    public static void resetListenerData(Context context) {
+        sendCommand(context, SERVICE_RESET, null, 0);
+    }
+
+    public static void probeListenerStatus(Context context, IntegerResultCatcher catcher) {
+        requestIntegerResult(context, SERVICE_CHECK, catcher);
+    }
+
+    public static void probeListenerPosted(Context context, StringListResultCatcher catcher) {
+        requestStringListResult(context, SERVICE_POSTED, catcher);
+    }
+
+    public static void probeListenerPayloads(Context context, StringListResultCatcher catcher) {
+        requestStringListResult(context, SERVICE_PAYLOADS, catcher);
+    }
+
+    public static void probeListenerRemoved(Context context, StringListResultCatcher catcher) {
+        requestStringListResult(context, SERVICE_REMOVED, catcher);
+    }
+
+    public static void clearOne(Context context, String tag, int code) {
+        sendCommand(context, SERVICE_CLEAR_ONE, tag, code);
+    }
+
+    public static void clearAll(Context context) {
+        sendCommand(context, SERVICE_CLEAR_ALL, null, 0);
+    }
+
+    private static void sendCommand(Context context, String action, String tag, int code) {
+        Intent broadcast = new Intent(action);
+        if (tag != null) {
+            broadcast.putExtra(EXTRA_TAG, tag);
+            broadcast.putExtra(EXTRA_CODE, code);
+        }
+        context.sendBroadcast(broadcast);
+    }
+
+    public abstract static class IntegerResultCatcher extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            accept(Integer.valueOf(getResultCode()));
+        }
+
+        abstract public void accept(int result);
+    }
+
+    private static void requestIntegerResult(Context context, String action,
+            IntegerResultCatcher catcher) {
+        Intent broadcast = new Intent(action);
+        context.sendOrderedBroadcast(broadcast, null, catcher, null, RESULT_NO_SERVER, null, null);
+    }
+
+    public abstract static class StringListResultCatcher extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            accept(getResultExtras(true).getStringArrayList(EXTRA_PAYLOAD));
+        }
+
+        abstract public void accept(List<String> result);
+    }
+
+    private static void requestStringListResult(Context context, String action,
+            StringListResultCatcher catcher) {
+        Intent broadcast = new Intent(action);
+        context.sendOrderedBroadcast(broadcast, null, catcher, null, RESULT_NO_SERVER, null, null);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nls/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nls/NotificationListenerVerifierActivity.java
new file mode 100644
index 0000000..7c64eb6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nls/NotificationListenerVerifierActivity.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2013 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.nls;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.provider.Settings.Secure;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.TagVerifierActivity;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class NotificationListenerVerifierActivity extends PassFailButtons.Activity
+implements Runnable {
+    static final String TAG = TagVerifierActivity.class.getSimpleName();
+    private static final String STATE = "state";
+    private static final String LISTENER_PATH = "com.android.cts.verifier/" + 
+            "com.android.cts.verifier.nls.MockListener";
+    private static final int PASS = 1;
+    private static final int FAIL = 2;
+    private static final int WAIT_FOR_USER = 3;
+    private static final int NOTIFICATION_ID = 1001;
+    private static LinkedBlockingQueue<String> sDeletedQueue = new LinkedBlockingQueue<String>();
+
+    private int mState = -1;
+    private int[] mStatus;
+    private LayoutInflater mInflater;
+    private ViewGroup mItemList;
+    private PackageManager mPackageManager;
+    private String mTag1;
+    private String mTag2;
+    private String mTag3;
+    private NotificationManager mNm;
+    private Context mContext;
+    private Runnable mRunner;
+    private View mHandler;
+    private String mIdString;
+    private String mPackageString;
+
+    public static class DismissService extends Service {
+        @Override
+        public IBinder onBind(Intent intent) {
+            return null;
+        }
+
+        @Override
+        public void onStart(Intent intent, int startId) {
+            sDeletedQueue.offer(intent.getAction());
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState != null) {
+            mState = savedInstanceState.getInt(STATE, -1);
+        }
+        mContext = this;
+        mRunner = this;
+        mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        mPackageManager = getPackageManager();
+        mInflater = getLayoutInflater();
+        View view = mInflater.inflate(R.layout.nls_main, null);
+        mItemList = (ViewGroup) view.findViewById(R.id.nls_test_items);
+        mHandler = mItemList;
+        createTestItems();
+        mStatus = new int[mItemList.getChildCount()];
+        setContentView(view);
+
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.nls_test, R.string.nls_info, -1);
+
+        getPassButton().setEnabled(false);
+    }
+
+    @Override
+    protected void onSaveInstanceState (Bundle outState) {
+        outState.putInt(STATE, mState);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mHandler.post(mRunner);
+    }
+
+    // Interface Utilities
+
+    private void createTestItems() {
+        createUserItem(R.string.nls_enable_service);
+        createAutoItem(R.string.nls_service_started);
+        createAutoItem(R.string.nls_note_received);
+        createAutoItem(R.string.nls_payload_intact);
+        createAutoItem(R.string.nls_clear_one);
+        createAutoItem(R.string.nls_clear_all);
+        createUserItem(R.string.nls_disable_service);
+        createAutoItem(R.string.nls_service_stopped);
+        createAutoItem(R.string.nls_note_missed);
+    }
+
+    private void setItemState(int index, boolean passed) {
+        if (index != -1) {
+            ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
+            ImageView status = (ImageView) item.findViewById(R.id.nls_status);
+            status.setImageResource(passed ? R.drawable.fs_good : R.drawable.fs_error);
+            View button = item.findViewById(R.id.nls_launch_settings);
+            button.setClickable(false);
+            button.setEnabled(false);
+            status.invalidate();
+        }
+    }
+
+    private View createUserItem(int stringId) {
+        View item = mInflater.inflate(R.layout.nls_item, mItemList, false);
+        TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
+        instructions.setText(stringId);
+        mItemList.addView(item);
+        return item;
+    }
+
+    private View createAutoItem(int stringId) {
+        View item = mInflater.inflate(R.layout.nls_item, mItemList, false);
+        TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
+        instructions.setText(stringId);
+        View button = item.findViewById(R.id.nls_launch_settings);
+        button.setVisibility(View.GONE);
+        mItemList.addView(item);
+        return item;
+    }
+
+    // Test management
+
+    public void run() {
+        while (mState >= 0 && mState < mStatus.length && mStatus[mState] != WAIT_FOR_USER) {
+            if (mStatus[mState] == PASS) {
+                setItemState(mState, true);
+                mState++;
+            } else if (mStatus[mState] == FAIL) {
+                setItemState(mState, false);
+                return;
+            } else {
+                break;
+            }
+        }
+
+        switch (mState) {
+            case -1:
+                mState++;
+                mHandler.post(mRunner);
+                break;
+            case 0:
+                testIsEnabled(0);
+                break;
+            case 1:
+                testIsStarted(1);
+                break;
+            case 2:
+                testNotificationRecieved(2);
+                break;
+            case 3:
+                testDataIntact(3);
+                break;
+            case 4:
+                testDismissOne(4);
+                break;
+            case 5:
+                testDismissAll(5);
+                break;
+            case 6:
+                testIsDisabled(6);
+                break;
+            case 7:
+                testIsStopped(7);
+                break;
+            case 8:
+                testNotificationNotRecieved(8);
+                break;
+            case 9:
+                getPassButton().setEnabled(true);
+                break;
+        }
+    }
+
+    public void launchSettings(View button) {
+        startActivity(
+                new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
+    }
+
+    private PendingIntent makeIntent(int code, String tag) {
+        Intent intent = new Intent(tag);
+        intent.setComponent(new ComponentName(mContext, DismissService.class));
+        PendingIntent pi = PendingIntent.getService(mContext, code, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        return pi;
+    }
+
+    @SuppressLint("NewApi")
+    private void sendNotificaitons() {
+        mTag1 = UUID.randomUUID().toString();
+        mTag2 = UUID.randomUUID().toString();
+        mTag3 = UUID.randomUUID().toString();
+
+        mNm.cancelAll();
+
+        Notification n1 = new Notification.Builder(mContext)
+        .setContentTitle("ClearTest 1")
+        .setContentText(mTag1.toString())
+        .setPriority(Notification.PRIORITY_LOW)
+        .setSmallIcon(R.drawable.fs_good)
+        .setDeleteIntent(makeIntent(1, mTag1))
+        .build();
+        mNm.notify(mTag1, NOTIFICATION_ID + 1, n1);
+        mIdString = Integer.toString(NOTIFICATION_ID + 1);
+        mPackageString = "com.android.cts.verifier";
+
+        Notification n2 = new Notification.Builder(mContext)
+        .setContentTitle("ClearTest 2")
+        .setContentText(mTag2.toString())
+        .setPriority(Notification.PRIORITY_LOW)
+        .setSmallIcon(R.drawable.fs_good)
+        .setDeleteIntent(makeIntent(2, mTag2))
+        .build();
+        mNm.notify(mTag2, NOTIFICATION_ID + 2, n2);
+
+        Notification n3 = new Notification.Builder(mContext)
+        .setContentTitle("ClearTest 3")
+        .setContentText(mTag3.toString())
+        .setPriority(Notification.PRIORITY_LOW)
+        .setSmallIcon(R.drawable.fs_good)
+        .setDeleteIntent(makeIntent(3, mTag3))
+        .build();
+        mNm.notify(mTag3, NOTIFICATION_ID + 3, n3);
+    }
+
+    // Tests
+
+    private void testIsEnabled(int i) {
+        Intent settings = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
+        if (settings.resolveActivity(mPackageManager) == null) {
+            mStatus[i] = FAIL;
+        } else {
+            // TODO: find out why Secure.ENABLED_NOTIFICATION_LISTENERS is hidden
+            String listeners = Secure.getString(getContentResolver(),
+                    "enabled_notification_listeners");
+            if (listeners != null && listeners.contains(LISTENER_PATH)) {
+                mStatus[i] = PASS;
+            } else {
+                mStatus[i] = WAIT_FOR_USER;
+            }
+        }
+        mHandler.postDelayed(mRunner, 2000);
+    }
+
+    private void testIsStarted(final int i) {
+        MockListener.resetListenerData(this);
+        MockListener.probeListenerStatus(mContext,
+                new MockListener.IntegerResultCatcher() {
+            @Override
+            public void accept(int result) {
+                if (result == Activity.RESULT_OK) {
+                    mStatus[i] = PASS;
+                    // setup for testNotificationRecieved
+                    sendNotificaitons();
+                } else {
+                    mStatus[i] = FAIL;
+                }
+                mHandler.postDelayed(mRunner, 2000);
+            }
+        });
+    }
+
+    private void testNotificationRecieved(final int i) {
+        MockListener.probeListenerPosted(mContext,
+                new MockListener.StringListResultCatcher() {
+            @Override
+            public void accept(List<String> result) {
+                if (result.size() > 0 && result.contains(mTag1)) {
+                    mStatus[i] = PASS;
+                } else {
+                    mStatus[i] = FAIL;
+                }
+                mHandler.post(mRunner);
+            }});
+    }
+
+    private void testDataIntact(final int i) {
+        MockListener.probeListenerPayloads(mContext,
+                new MockListener.StringListResultCatcher() {
+            @Override
+            public void accept(List<String> result) {
+                mStatus[i] = FAIL;
+                if (result.size() > 0) {
+                    for(String payload : result) {
+                        if (payload.contains(mTag1) &&
+                                payload.contains(mIdString) &&
+                                payload.contains(mPackageString)) {
+                            mStatus[i] = PASS;
+                        }
+                    }
+                }
+                // setup for testDismissOne
+                MockListener.resetListenerData(mContext);
+                MockListener.clearOne(mContext, mTag1, NOTIFICATION_ID + 1);
+                mHandler.postDelayed(mRunner, 1000);
+            }});
+    }
+
+    private void testDismissOne(final int i) {
+        MockListener.probeListenerRemoved(mContext,
+                new MockListener.StringListResultCatcher() {
+            @Override
+            public void accept(List<String> result) {
+                if (result.size() > 0 && result.contains(mTag1)) {
+                    mStatus[i] = PASS;
+                } else {
+                    mStatus[i] = FAIL;
+                }
+
+                // setup for testDismissAll
+                MockListener.resetListenerData(mContext);
+                MockListener.clearAll(mContext);
+                mHandler.postDelayed(mRunner, 1000);
+            }});
+    }
+
+    private void testDismissAll(final int i) {
+        MockListener.probeListenerRemoved(mContext,
+                new MockListener.StringListResultCatcher() {
+            @Override
+            public void accept(List<String> result) {
+                if (result.size() == 2 && result.contains(mTag2) && result.contains(mTag3)) {
+                    mStatus[i] = PASS;
+                } else {
+                    mStatus[i] = FAIL;
+                }
+                mHandler.post(mRunner);
+            }
+        });   
+    }
+
+    private void testIsDisabled(int i) {
+        MockListener.resetListenerData(this);
+        // TODO: find out why Secure.ENABLED_NOTIFICATION_LISTENERS is hidden
+        String listeners = Secure.getString(getContentResolver(),
+                "enabled_notification_listeners");
+        if (listeners == null || !listeners.contains(LISTENER_PATH)) {
+            mStatus[i] = PASS;
+        } else {
+            mStatus[i] = WAIT_FOR_USER;
+        }
+        mHandler.postDelayed(mRunner, 2000);
+    }
+
+    private void testIsStopped(final int i) {
+        MockListener.probeListenerStatus(mContext,
+                new MockListener.IntegerResultCatcher() {
+            @Override
+            public void accept(int result) {
+                if (result == Activity.RESULT_OK) {
+                    MockListener.resetListenerData(mContext);
+                    sendNotificaitons();
+                    mStatus[i] = FAIL;
+                } else {
+                    mStatus[i] = PASS;
+                }
+                // setup for testNotificationRecieved
+                sendNotificaitons();
+                mHandler.postDelayed(mRunner, 1000);
+            }
+        });
+    }
+
+    private void testNotificationNotRecieved(final int i) {
+        MockListener.probeListenerPosted(mContext,
+                new MockListener.StringListResultCatcher() {
+            @Override
+            public void accept(List<String> result) {
+                if (result == null || result.size() == 0) {
+                    mStatus[i] = PASS;
+                } else {
+                    mStatus[i] = FAIL;
+                }
+                mHandler.post(mRunner);
+            }});
+    }
+}
diff --git a/suite/pts/deviceTests/browserbench/assets/octane/index.html b/suite/pts/deviceTests/browserbench/assets/octane/index.html
index 704123f..9c072d6 100644
--- a/suite/pts/deviceTests/browserbench/assets/octane/index.html
+++ b/suite/pts/deviceTests/browserbench/assets/octane/index.html
@@ -73,7 +73,6 @@
     document.getElementById("progress-bar-container").style.visibility = 'hidden';
     document.getElementById("bottom-text").style.visibility = 'visible';
     document.getElementById("inside-anchor").removeChild(document.getElementById("bar-appendix"));
-    document.getElementById("warning-header").style.visibility = 'hidden';
   }
 
   function Run() {
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 75ba598..f9df338 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -339,6 +339,7 @@
         </activity>
 
         <activity android:name="android.widget.cts.VideoViewStubActivity"
+            android:configChanges="keyboardHidden|orientation|screenSize">
             android:label="VideoViewStubActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
index 3ebc567..30a1f67 100644
--- a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -40,7 +40,10 @@
 
     private static final String TAG = OpenGlEsVersionTest.class.getSimpleName();
 
+    // TODO: switch to android.opengl.EGL14/EGLExt and use the constants from there
+    private static final int EGL_OPENGL_ES_BIT = 0x0001;
     private static final int EGL_OPENGL_ES2_BIT = 0x0004;
+    private static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040;
 
     private OpenGlEsVersionStubActivity mActivity;
 
@@ -71,12 +74,27 @@
         }
     }
 
-    /** @return OpenGL ES major version 1 or 2 or some negative number for error */
-    private static int getDetectedVersion() {
+    private static boolean hasExtension(String extensions, String name) {
+        int start = extensions.indexOf(name);
+        while (start >= 0) {
+            // check that we didn't find a prefix of a longer extension name
+            int end = start + name.length();
+            if (end == extensions.length() || extensions.charAt(end) == ' ') {
+                return true;
+            }
+            start = extensions.indexOf(name, end);
+        }
+        return false;
+    }
 
+    /** @return OpenGL ES major version 1, 2, or 3 or some non-positive number for error */
+    private static int getDetectedVersion() {
         /*
-         * Get all the device configurations and check if any of the attributes specify the
-         * the EGL_OPENGL_ES2_BIT to determine whether the device supports 2.0.
+         * Get all the device configurations and check the EGL_RENDERABLE_TYPE attribute
+         * to determine the highest ES version supported by any config. The
+         * EGL_KHR_create_context extension is required to check for ES3 support; if the
+         * extension is not present this test will fail to detect ES3 support. This
+         * effectively makes the extension mandatory for ES3-capable devices.
          */
 
         EGL10 egl = (EGL10) EGLContext.getEGL();
@@ -85,15 +103,23 @@
 
         if (egl.eglInitialize(display, null)) {
             try {
+                boolean checkES3 = hasExtension(egl.eglQueryString(display, EGL10.EGL_EXTENSIONS),
+                        "EGL_KHR_create_context");
                 if (egl.eglGetConfigs(display, null, 0, numConfigs)) {
                     EGLConfig[] configs = new EGLConfig[numConfigs[0]];
                     if (egl.eglGetConfigs(display, configs, numConfigs[0], numConfigs)) {
+                        int highestEsVersion = 0;
                         int[] value = new int[1];
                         for (int i = 0; i < numConfigs[0]; i++) {
                             if (egl.eglGetConfigAttrib(display, configs[i],
                                     EGL10.EGL_RENDERABLE_TYPE, value)) {
-                                if ((value[0] & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT) {
-                                    return 2;
+                                if (checkES3 && ((value[0] & EGL_OPENGL_ES3_BIT_KHR) ==
+                                        EGL_OPENGL_ES3_BIT_KHR)) {
+                                    if (highestEsVersion < 3) highestEsVersion = 3;
+                                } else if ((value[0] & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT) {
+                                    if (highestEsVersion < 2) highestEsVersion = 2;
+                                } else if ((value[0] & EGL_OPENGL_ES_BIT) == EGL_OPENGL_ES_BIT) {
+                                    if (highestEsVersion < 1) highestEsVersion = 1;
                                 }
                             } else {
                                 Log.w(TAG, "Getting config attribute with "
@@ -102,7 +128,7 @@
                                         + egl.eglGetError());
                             }
                         }
-                        return 1;
+                        return highestEsVersion;
                     } else {
                         Log.e(TAG, "Getting configs with EGL10#eglGetConfigs failed: "
                                 + egl.eglGetError());
@@ -161,9 +187,13 @@
      * version and Y must be some digit.
      */
     private void assertGlVersionString(int majorVersion) throws InterruptedException {
+        String versionString = "" + majorVersion;
         String message = "OpenGL version string '" + mActivity.getVersionString()
                 + "' is not " + majorVersion + ".0+.";
-        assertTrue(message, Pattern.matches(".*OpenGL.*ES.*" + majorVersion + "\\.\\d.*",
+        if (majorVersion == 2) {
+             versionString = "(2|3)";
+        }
+        assertTrue(message, Pattern.matches(".*OpenGL.*ES.*" + versionString + "\\.\\d.*",
                 mActivity.getVersionString()));
     }
 
diff --git a/tests/tests/media/src/android/media/cts/InputSurface.java b/tests/tests/media/src/android/media/cts/InputSurface.java
index ff6ece1..36ec2c7 100644
--- a/tests/tests/media/src/android/media/cts/InputSurface.java
+++ b/tests/tests/media/src/android/media/cts/InputSurface.java
@@ -17,6 +17,7 @@
 package android.media.cts;
 
 import android.opengl.EGL14;
+import android.opengl.EGLExt;
 import android.opengl.EGLConfig;
 import android.opengl.EGLContext;
 import android.opengl.EGLDisplay;
@@ -169,7 +170,7 @@
      * Sends the presentation time stamp to EGL.
      */
     public void setPresentationTime(long when) {
-        EGL14.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, when);
+        EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, when);
     }
 
     /**
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index 888a768..d1b88a1 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -30,7 +30,7 @@
     private static final String LOG_TAG = "BuildVersionTest";
     private static final Set<String> EXPECTED_RELEASES =
             new HashSet<String>(Arrays.asList("4.3"));
-    private static final int EXPECTED_SDK = 17;
+    private static final int EXPECTED_SDK = 18;
 
     @SuppressWarnings("deprecation")
     public void testReleaseVersion() {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index 740e31b..e68286f 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -191,12 +191,21 @@
         assertNull(mContentResolver.query(Media.getContentUri(volume), null, null, null, null));
     }
 
+    private void cleanExternalMediaFile(String path) {
+        mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, "_data=?", new String[] { path });
+        new File(path).delete();
+    }
+
     public void testStoreImagesMediaExternal() throws Exception {
         final String externalPath = Environment.getExternalStorageDirectory().getPath() +
                 "/testimage.jpg";
         final String externalPath2 = Environment.getExternalStorageDirectory().getPath() +
                 "/testimage1.jpg";
 
+        // clean up any potential left over entries from a previous aborted run
+        cleanExternalMediaFile(externalPath);
+        cleanExternalMediaFile(externalPath2);
+
         int numBytes = 1337;
         FileUtils.createFile(new File(externalPath), numBytes);
 
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
index c3eb0b8..c9461b4 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
@@ -58,12 +58,21 @@
         assertNull(mContentResolver.query(Media.getContentUri(volume), null, null, null, null));
     }
 
+    private void cleanExternalMediaFile(String path) {
+        mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, "_data=?", new String[] { path });
+        new File(path).delete();
+    }
+
     public void testStoreVideoMediaExternal() throws Exception {
         final String externalVideoPath = Environment.getExternalStorageDirectory().getPath() +
                  "/video/testvideo.3gp";
         final String externalVideoPath2 = Environment.getExternalStorageDirectory().getPath() +
                 "/video/testvideo1.3gp";
 
+        // clean up any potential left over entries from a previous aborted run
+        cleanExternalMediaFile(externalVideoPath);
+        cleanExternalMediaFile(externalVideoPath2);
+
         int numBytes = 1337;
         File videoFile = new File(externalVideoPath);
         FileUtils.createFile(videoFile, numBytes);
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
index 8f6f729..287551c 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
@@ -105,6 +105,10 @@
 
     private Uri insertVideo() throws IOException {
         File file = new File(Environment.getExternalStorageDirectory(), "testVideo.3gp");
+        // clean up any potential left over entries from a previous aborted run
+        mResolver.delete(Media.EXTERNAL_CONTENT_URI,
+                "_data=?", new String[] { file.getAbsolutePath() });
+        file.delete();
         mFileHelper.copyToExternalStorage(R.raw.testvideo, file);
 
         ContentValues values = new ContentValues();