Merge "Make EncodeDecodeTest aware of crop rects." into jb-mr2-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index abab023..e75fcbb 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -453,6 +453,29 @@
             </intent-filter>
         </activity-alias>
 
+        <activity android:name=".widget.WidgetTestActivity"
+                android:label="@string/widget_framework_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_other" />
+        </activity>
+
+        <receiver android:name=".widget.WidgetCtsProvider">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <!-- This specifies the widget provider info -->
+            <meta-data android:name="android.appwidget.provider"
+                    android:resource="@xml/widget_info" />
+        </receiver>
+
+        <!-- The service serving the RemoteViews to the collection widget -->
+        <service android:name=".widget.WidgetCtsService"
+            android:permission="android.permission.BIND_REMOTEVIEWS"
+            android:exported="false" />
+
    </application>
 
 </manifest>
diff --git a/apps/CtsVerifier/res/layout/list_item.xml b/apps/CtsVerifier/res/layout/list_item.xml
new file mode 100644
index 0000000..7fd9002
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/list_item.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/list_item_text"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="8dp"
+    android:gravity="center"
+    android:fontFamily="sans-serif-light"
+    android:textSize="14dp"
+    android:freezesText="true"
+    android:background="#ff1f1f1f" />
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/pass_fail_widget.xml b/apps/CtsVerifier/res/layout/pass_fail_widget.xml
new file mode 100644
index 0000000..31691cc
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pass_fail_widget.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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">
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/widget_layout.xml b/apps/CtsVerifier/res/layout/widget_layout.xml
new file mode 100644
index 0000000..4e2a6b6
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/widget_layout.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginTop="@dimen/widget_margin_top"
+    android:layout_marginBottom="@dimen/widget_margin_bottom"
+    android:layout_marginLeft="@dimen/widget_margin_left"
+    android:layout_marginRight="@dimen/widget_margin_right"
+    android:padding="1dp"
+    android:background="#2f2f2f">
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingLeft="12dp"
+        android:paddingTop="12dp"
+        android:paddingBottom="12dp"
+        android:paddingRight="12dp"
+        android:layout_gravity="center"
+        android:background="#222">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="top|left"
+            android:layout_marginBottom="10dp"
+            android:fontFamily="sans-serif"
+            android:textSize="20sp"
+            android:text="@string/widget_name"
+            android:freezesText="true"/>
+
+        <TextView
+            android:id="@+id/instruction"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="20dp"
+            android:fontFamily="sans-serif-light"
+            android:textSize="16sp"
+            android:freezesText="true"/>
+
+        <TextView
+            android:id="@+id/data"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="18dp"
+            android:layout_gravity="center_horizontal"
+            android:fontFamily="sans-serif-light"
+            android:textSize="16sp"/>
+
+        <ListView
+            android:id="@+id/list"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:layout_marginBottom="12dp"
+            android:padding="1dp"
+            android:background="#333"
+            android:visibility="gone"/>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center_horizontal"
+            android:orientation="horizontal">
+            <Button
+                android:id="@+id/fail"
+                android:layout_marginRight="8dp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minWidth="100dp"
+                android:text="@string/widget_fail" />
+            <Button
+                android:id="@+id/pass"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minWidth="100dp"
+                android:text="@string/widget_pass" />
+        </LinearLayout>
+
+    </LinearLayout>
+</FrameLayout>
diff --git a/apps/CtsVerifier/res/values-v14/dimens.xml b/apps/CtsVerifier/res/values-v14/dimens.xml
new file mode 100644
index 0000000..8b5494e
--- /dev/null
+++ b/apps/CtsVerifier/res/values-v14/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <dimen name="widget_margin_top">0dp</dimen>
+    <dimen name="widget_margin_bottom">0dp</dimen>
+    <dimen name="widget_margin_left">0dp</dimen>
+    <dimen name="widget_margin_right">0dp</dimen>
+</resources>
diff --git a/apps/CtsVerifier/res/values/dimens.xml b/apps/CtsVerifier/res/values/dimens.xml
new file mode 100644
index 0000000..00257a9
--- /dev/null
+++ b/apps/CtsVerifier/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <dimen name="widget_margin_top">8dp</dimen>
+    <dimen name="widget_margin_bottom">8dp</dimen>
+    <dimen name="widget_margin_left">8dp</dimen>
+    <dimen name="widget_margin_right">8dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 18a681a..79db2b0 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -601,4 +601,12 @@
     <string name="nls_service_stopped">Service should stop once disabled.</string>
     <string name="nls_note_missed">Check that notification was not received.</string>
     
+    <!-- Strings for Widget -->
+    <string name="widget_framework_test">Widget Framework Test</string>
+    <string name="widget_framework_test_info">This test checks some basic features of the widget
+        framework. In order to perform the test, press the Home button. Add the widget
+        titled "CTS Verifier" to the home screen. Follow the instructions in the widget.</string>
+    <string name="widget_name">Widget Framework Test</string>
+    <string name="widget_pass">Pass</string>
+    <string name="widget_fail">Fail</string>
 </resources>
diff --git a/apps/CtsVerifier/res/xml/widget_info.xml b/apps/CtsVerifier/res/xml/widget_info.xml
new file mode 100644
index 0000000..f875446
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/widget_info.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<appwidget-provider
+  xmlns:android="http://schemas.android.com/apk/res/android"
+  android:minWidth="250dp"
+  android:minHeight="360dp"
+  android:updatePeriodMillis="1800000"
+  android:initialLayout="@layout/widget_layout"
+  android:resizeMode="vertical|horizontal"
+  android:minResizeWidth="40dp"
+  android:minResizeHeight="40dp"
+  android:widgetCategory="keyguard|home_screen">
+</appwidget-provider>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java
new file mode 100644
index 0000000..74146f1
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java
@@ -0,0 +1,322 @@
+/*
+ * 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.widget;
+
+import java.util.HashMap;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Pair;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import com.android.cts.verifier.R;
+
+/**
+ * The weather widget's AppWidgetProvider.
+ */
+public class WidgetCtsProvider extends AppWidgetProvider {
+    class TextData {
+        String title;
+        String instruction;
+        String dataP;
+        String dataL;
+
+        public TextData(String t, String i, String dp, String dl) {
+            title = t;
+            instruction = i;
+            dataL = dl;
+            dataP = dp;
+        }
+    }
+
+    public static String PASS = "com.example.android.widgetcts.PASS";
+    public static String FAIL = "com.example.android.widgetcts.FAIL";
+
+    public static final int STATE_BEGIN = 0;
+    public static final int STATE_VERIFY_SIZE_CALLBACK = 1;
+    public static final int STATE_VERIFY_RESIZE = 2;
+    public static final int STATE_VERIFY_COLLECTIONS = 3;
+    public static final int STATE_VERIFY_HOME_OR_KEYGUARD_CALLBACK = 4;
+    public static final int STATE_COMPLETE = 5;
+
+    // If relevant, we want to verify the size callback first, before any
+    // resizing.
+    static HashMap<Integer, Integer> sStateMap = new HashMap<Integer, Integer>();
+    static HashMap<Integer, Integer> sTestCount = new HashMap<Integer, Integer>();
+    static HashMap<Integer, Integer> sPassCount = new HashMap<Integer, Integer>();
+
+    private static int sSDKLevel = android.os.Build.VERSION.SDK_INT;
+
+    public WidgetCtsProvider() {
+    }
+
+    @Override
+    public void onReceive(Context ctx, Intent intent) {
+        final String action = intent.getAction();
+        if (action.equals(PASS) || action.equals(FAIL)) {
+            boolean pass = action.equals(PASS);
+
+            int widgetId = (Integer) intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
+                    -1);
+
+            if (sStateMap.get(widgetId) != STATE_BEGIN && sStateMap.get(widgetId)
+                    != STATE_COMPLETE) {
+                if (!sTestCount.containsKey(widgetId)) {
+                    sTestCount.put(widgetId, 0);
+                }
+                if (!sPassCount.containsKey(widgetId)) {
+                    sPassCount.put(widgetId, 0);
+                }
+
+                sPassCount.put(widgetId, sPassCount.get(widgetId) + (pass ? 1 : 0));
+                sTestCount.put(widgetId, sTestCount.get(widgetId) + 1);
+            }
+            gotoNextTest(widgetId);
+
+            AppWidgetManager mgr = AppWidgetManager.getInstance(ctx);
+            Bundle options = getAppWidgetOptions(mgr, widgetId);
+            updateWidget(ctx, widgetId, mgr, options);
+        }
+        super.onReceive(ctx, intent);
+    }
+
+    @Override
+    public void onDeleted(Context ctx, int[] ids) {
+        for (int i = 0; i < ids.length; i++) {
+            sStateMap.remove(ids[i]);
+        }
+    }
+
+    Bundle getAppWidgetOptions(AppWidgetManager mgr, int widgetId) {
+        if (sSDKLevel >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
+            return mgr.getAppWidgetOptions(widgetId);
+        }
+        return null;
+    }
+
+    void gotoNextTest(int widgetId) {
+        int state = sStateMap.get(widgetId);
+        boolean foundNextTest = false;
+        do {
+            state = state == STATE_COMPLETE ? state : state + 1;
+            foundNextTest = shouldPerformTest(state);
+        } while (state < STATE_COMPLETE && !foundNextTest);
+        sStateMap.put(widgetId, state);
+    }
+
+    private boolean shouldPerformTest(int state) {
+        if (state == STATE_VERIFY_SIZE_CALLBACK
+                && sSDKLevel < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+            return false;
+        } else if (state == STATE_VERIFY_RESIZE
+                && sSDKLevel < android.os.Build.VERSION_CODES.HONEYCOMB) {
+            return false;
+        } else if (state == STATE_VERIFY_COLLECTIONS
+                && sSDKLevel < android.os.Build.VERSION_CODES.HONEYCOMB) {
+            return false;
+        } else if (state == STATE_VERIFY_HOME_OR_KEYGUARD_CALLBACK
+                && sSDKLevel < android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+        for (int i = 0; i < appWidgetIds.length; i++) {
+            int id = appWidgetIds[i];
+            if (!sStateMap.containsKey(id)) {
+                sStateMap.put(id, STATE_BEGIN);
+            }
+            updateWidget(context, appWidgetIds[i], appWidgetManager, null);
+        }
+    }
+
+    @Override
+    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
+            int appWidgetId, Bundle newOptions) {
+        updateWidget(context, appWidgetId, appWidgetManager, newOptions);
+    }
+
+    private TextData getInstruction(int state, Bundle options, int widgetId) {
+        String title = null;
+        String instruction = null;
+        String dataL = null;
+        String dataP = null;
+
+        int widthP = -1;
+        int heightP = -1;
+        int widthL = -1;
+        int heightL = -1;
+        int category = -1;
+
+        // We use nullness of options as a proxy for an sdk check >= JB
+        if (options != null) {
+            widthP = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, -1);
+            heightP = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, -1);
+            widthL = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, -1);
+            heightL = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, -1);
+            category = options.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);
+        }
+
+        int step = 1;
+        if (sTestCount.containsKey(widgetId)) {
+            step = sTestCount.get(widgetId) + 1;
+        }
+        if (state == STATE_BEGIN) {
+            instruction = "This is a test of the widget framework";
+        } else if (state == STATE_VERIFY_SIZE_CALLBACK) {
+            title = "Step " + step + ": Verify dimensions";
+            instruction = "Verify that the width and height indicated below constitute reasonable"
+                    + " approximations of the widget's actual size:";
+            dataP = "Width: " + widthP + "     Height: " + heightP;
+            dataL = "Width: " + widthL + "     Height: " + heightL;
+        } else if (state == STATE_VERIFY_RESIZE) {
+            title = "Step " + step + ": Verify resizeability";
+            instruction = "Verify that there is a functional affordance which allows this widget"
+                    + " to be resized. For example, when picked up and dropped, there may be a "
+                    + " frame with handles. (This is not a requirement for widgets hosted on "
+                    + " a tablet keyguard).";
+            if (sSDKLevel >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
+                instruction = instruction
+                        + " Also, verify that after resize, the width and height below "
+                        + "are updated accordingly.";
+                dataP = "Width: " + widthP + "     Height: " + heightP;
+                dataL = "Width: " + widthL + "     Height: " + heightL;
+            }
+        } else if (state == STATE_VERIFY_COLLECTIONS) {
+            title = "Step " + step + ": Verify collections";
+            instruction = "Verify that the widget contains a scrollable list of numbers from 1"
+                    + " to " + WidgetCtsService.NUM_ITEMS;
+        } else if (state == STATE_VERIFY_HOME_OR_KEYGUARD_CALLBACK) {
+            title = "Step " + step + ": Verify category";
+            instruction = "Verify that the text below accurately reflects whether this widget is"
+                    + " on the home screen or the lock screen. ";
+            if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) {
+                dataL = dataP = "Widget is reportedly on: KEYGUARD";
+            } else if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN) {
+                dataL = dataP = "Widget is reportedly on: HOME SCREEN";
+            } else {
+                dataL = dataP = "Error.";
+            }
+        } else if (state == STATE_COMPLETE) {
+            title = "Test Complete";
+            instruction = "This completes the test of the widget framework. " +
+                    "Remove and re-add this widget to restart the test.";
+            dataL = dataP = sPassCount.get(widgetId) + " of " + sTestCount.get(widgetId)
+                    + " tests passed successfully.";
+        }
+        return new TextData(title, instruction, dataP, dataL);
+    }
+
+    private void updateWidget(Context context, int appWidgetId, AppWidgetManager appWidgetManager,
+            Bundle newOptions) {
+        // Pull them from the manager
+        if (newOptions == null) {
+            newOptions = getAppWidgetOptions(appWidgetManager, appWidgetId);
+        }
+
+        int baseLayout = R.layout.widget_layout;
+
+        RemoteViews rv = new RemoteViews(context.getPackageName(), baseLayout);
+        int state = sStateMap.get(appWidgetId);
+
+        TextData text = getInstruction(state, newOptions, appWidgetId);
+        rv.setTextViewText(R.id.instruction, text.instruction);
+
+        // Update the title
+        if (text.title != null) {
+            rv.setTextViewText(R.id.title, text.title);
+        }
+
+        if (state == STATE_VERIFY_COLLECTIONS) {
+            // Specify the service to provide data for the collection widget.
+            // Note that we need to
+            // embed the appWidgetId via the data otherwise it will be ignored.
+            final Intent intent = new Intent(context, WidgetCtsService.class);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+            rv.setViewVisibility(R.id.list, View.VISIBLE);
+            rv.setRemoteAdapter(appWidgetId, R.id.list, intent);
+        } else {
+            rv.setViewVisibility(R.id.list, View.GONE);
+        }
+
+        if (state == STATE_BEGIN) {
+            rv.setViewVisibility(R.id.fail, View.GONE);
+            rv.setTextViewText(R.id.pass, "Start Test");
+        } else if (state == STATE_COMPLETE) {
+            rv.setViewVisibility(R.id.fail, View.GONE);
+            rv.setViewVisibility(R.id.pass, View.GONE);
+        } else {
+            rv.setViewVisibility(R.id.fail, View.VISIBLE);
+            rv.setTextViewText(R.id.pass, "Pass");
+        }
+
+        final Intent pass = new Intent(context, WidgetCtsProvider.class);
+        pass.setAction(WidgetCtsProvider.PASS);
+        pass.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+        pass.setData(Uri.parse(pass.toUri(Intent.URI_INTENT_SCHEME)));
+        final PendingIntent passPendingIntent = PendingIntent.getBroadcast(context, 0, pass,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        final Intent fail = new Intent(context, WidgetCtsProvider.class);
+        fail.setAction(WidgetCtsProvider.FAIL);
+        fail.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+        fail.setData(Uri.parse(fail.toUri(Intent.URI_INTENT_SCHEME)));
+        final PendingIntent failPendingIntent = PendingIntent.getBroadcast(context, 0, fail,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        rv.setOnClickPendingIntent(R.id.pass, passPendingIntent);
+        rv.setOnClickPendingIntent(R.id.fail, failPendingIntent);
+
+        RemoteViews rvL = null;
+        if (text.dataP != null && !text.dataP.equals(text.dataL)) {
+            rvL = rv.clone();
+
+            System.out.println("hmmmm ok, made it innnnn");
+            if (text.dataL != null) {
+                rvL.setViewVisibility(R.id.data, View.VISIBLE);
+                rvL.setTextViewText(R.id.data, text.dataL);
+            } else {
+                rvL.setViewVisibility(R.id.data, View.GONE);
+            }
+        }
+
+        // Update the data
+        if (text.dataP != null) {
+            rv.setViewVisibility(R.id.data, View.VISIBLE);
+            rv.setTextViewText(R.id.data, text.dataP);
+        } else {
+            rv.setViewVisibility(R.id.data, View.GONE);
+        }
+
+        RemoteViews rvFinal = rv;
+        if (rvL != null) {
+            rvFinal = new RemoteViews(rvL, rv);
+        }
+
+        appWidgetManager.updateAppWidget(appWidgetId, rvFinal);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsService.java
new file mode 100644
index 0000000..7d210a2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsService.java
@@ -0,0 +1,88 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.Intent;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+import com.android.cts.verifier.R;
+
+/**
+ * This is the service that provides the factory to be bound to the collection
+ * service.
+ */
+public class WidgetCtsService extends RemoteViewsService {
+    public static final int NUM_ITEMS = 50;
+
+    @Override
+    public RemoteViewsFactory onGetViewFactory(Intent intent) {
+        return new CtsRemoteViewsFactory(this.getApplicationContext(), intent);
+    }
+}
+
+/**
+ * This is the factory that will provide data to the collection widget.
+ */
+class CtsRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
+
+    Context mContext;
+
+    public CtsRemoteViewsFactory(Context context, Intent intent) {
+        mContext = context;
+    }
+
+    public void onCreate() {
+    }
+
+    public void onDestroy() {
+    }
+
+    public int getCount() {
+        return WidgetCtsService.NUM_ITEMS;
+    }
+
+    public RemoteViews getViewAt(int position) {
+        RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.list_item);
+        String text = "List Item " + (position + 1);
+        rv.setTextViewText(R.id.list_item_text, text);
+        return rv;
+    }
+
+    public RemoteViews getLoadingView() {
+        // We aren't going to return a default loading view in this sample
+        return null;
+    }
+
+    public int getViewTypeCount() {
+        // Technically, we have two types of views (the dark and light
+        // background views)
+        return 1;
+    }
+
+    public long getItemId(int position) {
+        return position;
+    }
+
+    public boolean hasStableIds() {
+        return true;
+    }
+
+    public void onDataSetChanged() {
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetTestActivity.java
new file mode 100644
index 0000000..25f0a7a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetTestActivity.java
@@ -0,0 +1,52 @@
+/*
+ * 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.widget;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+
+/**
+ * CTS Verifier case for verifying basic widget framework functionality.
+ */
+public class WidgetTestActivity extends PassFailButtons.Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.pass_fail_widget);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.widget_framework_test, R.string.widget_framework_test_info, -1);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+}
diff --git a/tests/src/android/os/cts/FileUtils.java b/tests/src/android/os/cts/FileUtils.java
index 6b71b86..ea31c54 100644
--- a/tests/src/android/os/cts/FileUtils.java
+++ b/tests/src/android/os/cts/FileUtils.java
@@ -25,6 +25,7 @@
 /** Bits and pieces copied from hidden API of android.os.FileUtils. */
 public class FileUtils {
 
+    public static final int S_IFMT  = 0170000;
     public static final int S_IFSOCK = 0140000;
     public static final int S_IFLNK = 0120000;
     public static final int S_IFREG = 0100000;
@@ -73,8 +74,18 @@
         public long ctime;
 
         public boolean hasModeFlag(int flag) {
+            if (((S_IRWXU | S_IRWXG | S_IRWXO) & flag) != flag) {
+                throw new IllegalArgumentException("Inappropriate flag " + flag);
+            }
             return (mode & flag) == flag;
         }
+
+        public boolean isOfType(int type) {
+            if ((type & S_IFMT) != type) {
+                throw new IllegalArgumentException("Unknown type " + type);
+            }
+            return (mode & S_IFMT) == type;
+        }
     }
 
     /**
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 3abcbb6..2a9a2f2 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -660,30 +660,40 @@
         assertTrue("Cannot find /system partition", foundSystem);
     }
 
-    private static final Set<File> DEV_EXCEPTIONS = new HashSet<File>(
+    public void testAllBlockDevicesAreSecure() throws Exception {
+        Set<File> insecure = getAllInsecureDevicesInDirAndSubdir(new File("/dev"), FileUtils.S_IFBLK);
+        assertTrue("Found insecure block devices: " + insecure.toString(),
+                insecure.isEmpty());
+    }
+
+    private static final Set<File> CHAR_DEV_EXCEPTIONS = new HashSet<File>(
             Arrays.asList(
-                // Known good devices- should be present everywhere
+                // All exceptions should be alphabetical and associated with a bug number.
+                new File("/dev/alarm"),      // b/9035217
                 new File("/dev/ashmem"),
                 new File("/dev/binder"),
                 new File("/dev/full"),
                 new File("/dev/ion"),
+                new File("/dev/log/events"), // b/9035217
+                new File("/dev/log/main"),   // b/9035217
+                new File("/dev/log/radio"),  // b/9035217
+                new File("/dev/log/system"), // b/9035217
                 new File("/dev/null"),
                 new File("/dev/random"),
                 new File("/dev/tty"),
                 new File("/dev/urandom"),
                 new File("/dev/zero")
-                // Other exceptions go below here, along with a bug #
             ));
 
-    public void testAllDevicesAreSecure() throws Exception {
-        Set<File> insecure = getAllInsecureDevicesInDirAndSubdir(new File("/dev"));
-        insecure.removeAll(DEV_EXCEPTIONS);
-        assertTrue("Found insecure: " + insecure.toString(),
+    public void testAllCharacterDevicesAreSecure() throws Exception {
+        Set<File> insecure = getAllInsecureDevicesInDirAndSubdir(new File("/dev"), FileUtils.S_IFCHR);
+        insecure.removeAll(CHAR_DEV_EXCEPTIONS);
+        assertTrue("Found insecure character devices: " + insecure.toString(),
                 insecure.isEmpty());
     }
 
     private static Set<File>
-    getAllInsecureDevicesInDirAndSubdir(File dir) throws Exception {
+    getAllInsecureDevicesInDirAndSubdir(File dir, int type) throws Exception {
         assertTrue(dir.isDirectory());
         Set<File> retval = new HashSet<File>();
 
@@ -702,7 +712,7 @@
         /* recurse into subdirectories */
         if (subDirectories != null) {
             for (File f : subDirectories) {
-                retval.addAll(getAllInsecureDevicesInDirAndSubdir(f));
+                retval.addAll(getAllInsecureDevicesInDirAndSubdir(f, type));
             }
         }
 
@@ -714,7 +724,7 @@
         for (File f: filesInThisDirectory) {
             FileUtils.FileStatus status = new FileUtils.FileStatus();
             FileUtils.getFileStatus(f.getAbsolutePath(), status, false);
-            if (status.hasModeFlag(FileUtils.S_IFBLK) || status.hasModeFlag(FileUtils.S_IFCHR)) {
+            if (status.isOfType(type)) {
                 if (f.canRead() || f.canWrite() || f.canExecute()) {
                     retval.add(f);
                 }
diff --git a/tests/tests/permission/src/android/permission/cts/FileUtils.java b/tests/tests/permission/src/android/permission/cts/FileUtils.java
index 5a869de..56e773a 100644
--- a/tests/tests/permission/src/android/permission/cts/FileUtils.java
+++ b/tests/tests/permission/src/android/permission/cts/FileUtils.java
@@ -19,6 +19,7 @@
 /** Bits and pieces copied from hidden API of android.os.FileUtils. */
 public class FileUtils {
 
+    public static final int S_IFMT  = 0170000;
     public static final int S_IFSOCK = 0140000;
     public static final int S_IFLNK = 0120000;
     public static final int S_IFREG = 0100000;
@@ -31,14 +32,17 @@
     public static final int S_ISGID = 0002000;
     public static final int S_ISVTX = 0001000;
 
+    public static final int S_IRWXU = 00700;
     public static final int S_IRUSR = 00400;
     public static final int S_IWUSR = 00200;
     public static final int S_IXUSR = 00100;
 
+    public static final int S_IRWXG = 00070;
     public static final int S_IRGRP = 00040;
     public static final int S_IWGRP = 00020;
     public static final int S_IXGRP = 00010;
 
+    public static final int S_IRWXO = 00007;
     public static final int S_IROTH = 00004;
     public static final int S_IWOTH = 00002;
     public static final int S_IXOTH = 00001;
@@ -64,8 +68,18 @@
         public long ctime;
 
         public boolean hasModeFlag(int flag) {
+            if (((S_IRWXU | S_IRWXG | S_IRWXO) & flag) != flag) {
+                throw new IllegalArgumentException("Inappropriate flag " + flag);
+            }
             return (mode & flag) == flag;
         }
+
+        public boolean isOfType(int type) {
+            if ((type & S_IFMT) != type) {
+                throw new IllegalArgumentException("Unknown type " + type);
+            }
+            return (mode & S_IFMT) == type;
+        }
     }
 
     /**
diff --git a/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
index 21321c0..6551b52 100644
--- a/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
@@ -21,6 +21,7 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.cts.util.PollingCheck;
 import android.test.ActivityInstrumentationTestCase2;
 import android.text.InputType;
 import android.text.method.DateKeyListener;
@@ -47,6 +48,12 @@
         mActivity = getActivity();
         mInstrumentation = getInstrumentation();
         mTextView = (TextView) mActivity.findViewById(R.id.keylistener_textview);
+        new PollingCheck(1000) {
+            @Override
+            protected boolean check() {
+                return mTextView.hasWindowFocus();
+            }
+        }.run();
     }
 
     public void testConstructor() {
diff --git a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
index ea50294..16d9364 100644
--- a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
@@ -21,6 +21,7 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.cts.util.PollingCheck;
 import android.test.ActivityInstrumentationTestCase2;
 import android.text.InputType;
 import android.text.Spannable;
@@ -50,6 +51,12 @@
         mActivity = getActivity();
         mInstrumentation = getInstrumentation();
         mTextView = (TextView) mActivity.findViewById(R.id.keylistener_textview);
+        new PollingCheck(1000) {
+            @Override
+            protected boolean check() {
+                return mTextView.hasWindowFocus();
+            }
+        }.run();
     }
 
     public void testConstructor() {
diff --git a/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java
index b8bf7ef..795382c 100644
--- a/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java
@@ -21,6 +21,7 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.cts.util.PollingCheck;
 import android.test.ActivityInstrumentationTestCase2;
 import android.text.Editable;
 import android.text.Selection;
@@ -52,6 +53,12 @@
         mActivity = getActivity();
         mInstrumentation = getInstrumentation();
         mTextView = (TextView) mActivity.findViewById(R.id.keylistener_textview);
+        new PollingCheck(1000) {
+            @Override
+            protected boolean check() {
+                return mTextView.hasWindowFocus();
+            }
+        }.run();
     }
 
     /**
diff --git a/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
index 6d647b2..471f67a 100755
--- a/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
@@ -49,7 +49,7 @@
 
     private boolean isPasswordPrefSaved;
 
-    private StubActivity mActicity;
+    private StubActivity mActivity;
 
     private MockPasswordTransformationMethod mMethod;
 
@@ -64,22 +64,28 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mActicity = getActivity();
+        mActivity = getActivity();
+        new PollingCheck(1000) {
+            @Override
+            protected boolean check() {
+                return mActivity.hasWindowFocus();
+            }
+        }.run();
         mMethod = new MockPasswordTransformationMethod();
         try {
             runTestOnUiThread(new Runnable() {
                 public void run() {
-                    EditText editText = new EditText(mActicity);
+                    EditText editText = new EditText(mActivity);
                     editText.setId(EDIT_TXT_ID);
                     editText.setTransformationMethod(mMethod);
-                    Button button = new Button(mActicity);
-                    LinearLayout layout = new LinearLayout(mActicity);
+                    Button button = new Button(mActivity);
+                    LinearLayout layout = new LinearLayout(mActivity);
                     layout.setOrientation(LinearLayout.VERTICAL);
                     layout.addView(editText, new LayoutParams(LayoutParams.MATCH_PARENT,
                             LayoutParams.WRAP_CONTENT));
                     layout.addView(button, new LayoutParams(LayoutParams.MATCH_PARENT,
                             LayoutParams.WRAP_CONTENT));
-                    mActicity.setContentView(layout);
+                    mActivity.setContentView(layout);
                     editText.requestFocus();
                 }
             });
@@ -195,7 +201,7 @@
 
     private void savePasswordPref() {
         try {
-            mPasswordPrefBackUp = System.getInt(mActicity.getContentResolver(),
+            mPasswordPrefBackUp = System.getInt(mActivity.getContentResolver(),
                     System.TEXT_SHOW_PASSWORD);
             isPasswordPrefSaved = true;
         } catch (SettingNotFoundException e) {
@@ -205,13 +211,13 @@
 
     private void resumePasswordPref() {
         if (isPasswordPrefSaved) {
-            System.putInt(mActicity.getContentResolver(), System.TEXT_SHOW_PASSWORD,
+            System.putInt(mActivity.getContentResolver(), System.TEXT_SHOW_PASSWORD,
                     mPasswordPrefBackUp);
         }
     }
 
     private void switchShowPassword(boolean on) {
-        System.putInt(mActicity.getContentResolver(), System.TEXT_SHOW_PASSWORD,
+        System.putInt(mActivity.getContentResolver(), System.TEXT_SHOW_PASSWORD,
                 on ? 1 : 0);
     }
 
diff --git a/tests/tests/text/src/android/text/method/cts/TextKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/TextKeyListenerTest.java
index a95267a..9984efe 100644
--- a/tests/tests/text/src/android/text/method/cts/TextKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TextKeyListenerTest.java
@@ -20,6 +20,7 @@
 
 
 import android.app.Instrumentation;
+import android.cts.util.PollingCheck;
 import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
@@ -56,6 +57,12 @@
         mActivity = getActivity();
         mInstrumentation = getInstrumentation();
         mTextView = (TextView) mActivity.findViewById(R.id.keylistener_textview);
+        new PollingCheck(1000) {
+            @Override
+            protected boolean check() {
+                return mTextView.hasWindowFocus();
+            }
+        }.run();
     }
 
     public void testConstructor() {
diff --git a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
index cf80f8d..f9c172b 100644
--- a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
@@ -21,6 +21,7 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.cts.util.PollingCheck;
 import android.test.ActivityInstrumentationTestCase2;
 import android.text.InputType;
 import android.text.method.TimeKeyListener;
@@ -45,6 +46,12 @@
         mActivity = getActivity();
         mInstrumentation = getInstrumentation();
         mTextView = (TextView) mActivity.findViewById(R.id.keylistener_textview);
+        new PollingCheck(1000) {
+            @Override
+            protected boolean check() {
+                return mTextView.hasWindowFocus();
+            }
+        }.run();
     }
 
     public void testConstructor() {