Merge "Make tests more reliable by cleaning up worker threads on cancelation. b/17888343" into lmp-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 33531c8..ce67d37 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -15,6 +15,8 @@
 cts_security_apps_list := \
     CtsAppAccessData \
     CtsAppWithData \
+    CtsDocumentProvider \
+    CtsDocumentClient \
     CtsExternalStorageApp \
     CtsInstrumentationAppDiffCert \
     CtsPermissionDeclareApp \
@@ -40,6 +42,22 @@
     CtsWriteExternalStorageApp \
     CtsMultiUserStorageApp
 
+cts_security_keysets_list := \
+    CtsKeySetTestApp \
+    CtsKeySetPermDefSigningA \
+    CtsKeySetPermDefSigningB\
+    CtsKeySetPermUseSigningA \
+    CtsKeySetPermUseSigningB \
+    CtsKeySetSigningAUpgradeA \
+    CtsKeySetSigningBUpgradeA \
+    CtsKeySetSigningAUpgradeAAndB \
+    CtsKeySetSigningAUpgradeAOrB \
+    CtsKeySetSigningAUpgradeB \
+    CtsKeySetSigningBUpgradeB \
+    CtsKeySetSigningAAndBUpgradeA \
+    CtsKeySetSigningAAndCUpgradeB \
+    CtsKeySetSigningAUpgradeNone
+
 cts_support_packages := \
     CtsAccelerationTestStubs \
     CtsAppTestStubs \
@@ -59,7 +77,8 @@
     TestDeviceSetup \
     CtsUiAutomatorApp \
     CtsUsbSerialTestApp \
-    $(cts_security_apps_list)
+    $(cts_security_apps_list) \
+    $(cts_security_keysets_list)
 
 cts_external_packages := \
     com.replica.replicaisland \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index f37e1fa..bba42ad 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -349,17 +349,6 @@
             <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
         </activity>
 
-        <activity android:name=".bluetooth.BleScannerPrivacyMacActivity"
-                android:label="@string/ble_privacy_mac_name"
-                android:configChanges="keyboardHidden|orientation|screenSize">
-            <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/bt_le" />
-            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleScannerTestActivity" />
-        </activity>
-
         <activity android:name=".bluetooth.BleScannerPowerLevelActivity"
                 android:label="@string/ble_power_level_name"
                 android:configChanges="keyboardHidden|orientation|screenSize">
@@ -393,17 +382,6 @@
             <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
         </activity>
 
-        <activity android:name=".bluetooth.BleAdvertiserPrivacyMacActivity"
-                android:label="@string/ble_privacy_mac_name"
-                android:configChanges="keyboardHidden|orientation|screenSize">
-            <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/bt_le" />
-            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity" />
-        </activity>
-
         <activity android:name=".bluetooth.BleAdvertiserPowerLevelActivity"
                 android:label="@string/ble_power_level_name"
                 android:configChanges="keyboardHidden|orientation|screenSize">
@@ -835,6 +813,8 @@
                 <category android:name="android.cts.intent.category.MANUAL_TEST"/>
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_sensors"/>
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback" />
         </activity>
 
         <activity android:name=".sensors.SensorIntegrationTestsActivity"
diff --git a/apps/CtsVerifier/res/layout/ble_advertiser_privacy_mac.xml b/apps/CtsVerifier/res/layout/ble_advertiser_privacy_mac.xml
deleted file mode 100644
index 1c68b98..0000000
--- a/apps/CtsVerifier/res/layout/ble_advertiser_privacy_mac.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?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="match_parent"
-        android:orientation="vertical"
-        android:padding="10dip"
-        >
-    <TextView android:text="@string/ble_advertiser_privacy_mac_instruction"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-    />
-    <LinearLayout android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_centerInParent="true"
-            >
-        <Button android:id="@+id/ble_privacy_mac_start"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/ble_advertiser_start"
-                />
-        <Button android:id="@+id/ble_privacy_mac_stop"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/ble_advertiser_stop"
-                />
-    </LinearLayout>
-
-    <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/ble_scanner_power_level.xml b/apps/CtsVerifier/res/layout/ble_scanner_power_level.xml
index 970b03a..b240db6 100644
--- a/apps/CtsVerifier/res/layout/ble_scanner_power_level.xml
+++ b/apps/CtsVerifier/res/layout/ble_scanner_power_level.xml
@@ -22,11 +22,14 @@
     <TextView android:text="@string/ble_scanner_power_level_instruction"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:id="@+id/ble_scanner_power_level_instruction"
     />
     <LinearLayout android:orientation="vertical"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:layout_below="@+id/ble_scanner_power_level_instruction"
             android:layout_centerInParent="true"
+            android:padding="10dp"
             >
         <LinearLayout android:orientation="horizontal"
                 android:layout_width="match_parent"
@@ -156,6 +159,9 @@
                     android:layout_height="wrap_content"
             />
         </LinearLayout>
+        <TextView android:id="@+id/ble_timer"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content" />
     </LinearLayout>
 
     <include android:layout_width="match_parent"
diff --git a/apps/CtsVerifier/res/layout/ble_scanner_privacy_mac.xml b/apps/CtsVerifier/res/layout/ble_scanner_privacy_mac.xml
deleted file mode 100644
index cad78a3..0000000
--- a/apps/CtsVerifier/res/layout/ble_scanner_privacy_mac.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?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="match_parent"
-        android:orientation="vertical"
-        android:padding="10dip"
-        >
-
-    <TextView
-            android:text="@string/ble_scanner_privacy_mac_instruction"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-    />
-    <LinearLayout android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_centerInParent="true"
-            >
-        <LinearLayout android:orientation="horizontal"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_centerInParent="true"
-                >
-            <TextView android:text="@string/ble_address"
-                  android:layout_width="100dp"
-                  android:layout_height="wrap_content"
-            />
-            <TextView android:id="@+id/ble_mac_address"
-                  android:layout_weight="1"
-                  android:layout_width="0dp"
-                  android:layout_height="wrap_content"
-            />
-        </LinearLayout>
-        <LinearLayout android:orientation="horizontal"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_centerInParent="true"
-                >
-            <TextView android:id="@+id/ble_mac_count"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-            />
-            <TextView android:id="@+id/ble_resp_count"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_below="@+id/ble_mac_count"
-            />
-        </LinearLayout>
-        <LinearLayout android:orientation="horizontal"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_centerInParent="true"
-                >
-            <TextView android:id="@+id/ble_timer"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-            />
-        </LinearLayout>
-    </LinearLayout>
-
-    <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/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 879443f..3b74e52 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -238,7 +238,7 @@
     <string name="ble_power_level_info">BLE Advertiser advertises in 4 different power levels. Scanner should receive them in different strength of Rssi, cannot receive weak signals beyond several feet.</string>
     <string name="ble_advertiser_power_level_instruction">Click start to start multi-advertising. Data packets are advertised in 4 different power levels. You may receive message that this device does not support multi advertising. If advertiser does not advertise in 4 power levels, neither you receive the error message, you may not stop the advertising in previous test, or this device does not support 4 advertisers at the same time. Try rebooting the device and run the test to free those advertisers in use.</string>
     <string name="ble_advertiser_scan_filter_name">BLE Hardware Scan Filter</string>
-    <string name="ble_advertiser_scan_filter_info">BLE Advertiser advertises with 2 different data separately. One can wake up the scanner, the other cannot.</string>
+    <string name="ble_advertiser_scan_filter_info">BLE Advertiser advertises with 2 different data separately. One can wake up the scanner, the other cannot. This test cares about behavior on scanner only.</string>
     <string name="ble_advertiser_scannable">Scannable advertising</string>
     <string name="ble_advertiser_scannable_instruction">Start scannable advertising, expect scanner consume more power on Monsoon monitor, or see log of GattService from scanner logcat.</string>
     <string name="ble_advertiser_unscannable">Unscannble advertising</string>
@@ -256,7 +256,7 @@
     <string name="ble_low">Low</string>
     <string name="ble_medium">Medium</string>
     <string name="ble_high">High</string>
-    <string name="ble_scanner_power_level_instruction">Count: Ultra low &lt; low &lt; medium &lt; high\nRssi: Ultra low &lt; low &lt; medium &lt; high\nDistance to see count freezing: Ultra low &lt; low &lt; medium &lt; high\nA common error is ultra low, low and medium behave similarly, with similar rssi, freeze at similar distance.</string>
+    <string name="ble_scanner_power_level_instruction">Count: Ultra low &lt; low &lt; medium &lt; high\nRssi: Ultra low &lt; low &lt; medium &lt; high\nDistance to see count freezing: Ultra low &lt; low &lt; medium &lt; high\nA common error is ultra low, low and medium behave similarly, with similar rssi, freeze at similar distance.\n\n All power level receive a different mac address. After 15 mins, a green text "Get a new Mac address" will show up.</string>
     <string name="ble_scanner_scan_filter_name">BLE Hardware Scan Filter</string>
     <string name="ble_scanner_scan_filter_info">Lock the screen of scanner, and connect to monsoon. It will not wake up when advertiser is advertising unscannable, and scanner is scanning with filter.</string>
     <string name="ble_scanner_scan_filter_instruction">For monsoon test:\n\tClick scan with filter, lock the screen, connect to monsoon. It will not wake up when advertiser is advertising unscannable data packets, but will show a peak in power usage when advertiser is advertising scannable data.\nFor logcat test:\n\tClick scan with filter, logcat the scanner. No data will be received by GattService when advertiser is advertising unscannable data.</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserPrivacyMacActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserPrivacyMacActivity.java
deleted file mode 100644
index 87879bd..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserPrivacyMacActivity.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.bluetooth;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.Toast;
-
-public class BleAdvertiserPrivacyMacActivity extends PassFailButtons.Activity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.ble_advertiser_privacy_mac);
-        setPassFailButtonClickListeners();
-        setInfoResources(R.string.ble_privacy_mac_name,
-                         R.string.ble_privacy_mac_info, -1);
-
-        ((Button) findViewById(R.id.ble_privacy_mac_start))
-            .setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    Intent intent = new Intent(BleAdvertiserPrivacyMacActivity.this,
-                                               BleAdvertiserService.class);
-                    intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
-                                    BleAdvertiserService.COMMAND_START_ADVERTISE);
-                    startService(intent);
-                }
-            });
-        ((Button) findViewById(R.id.ble_privacy_mac_stop))
-            .setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    stopAdvertising();
-                }
-            });
-
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(BleAdvertiserService.BLE_START_ADVERTISE);
-        filter.addAction(BleAdvertiserService.BLE_STOP_ADVERTISE);
-        registerReceiver(onBroadcast, filter);
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        unregisterReceiver(onBroadcast);
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        stopAdvertising();
-    }
-
-    private void showMessage(String msg) {
-        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
-    }
-
-    private void stopAdvertising() {
-        Intent intent = new Intent(BleAdvertiserPrivacyMacActivity.this,
-                                   BleAdvertiserService.class);
-        intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
-                        BleAdvertiserService.COMMAND_STOP_ADVERTISE);
-        startService(intent);
-    }
-
-    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            switch (intent.getAction()) {
-                case BleAdvertiserService.BLE_START_ADVERTISE:
-                    showMessage("Start advertising, please hold for 15 min");
-                    break;
-                case BleAdvertiserService.BLE_STOP_ADVERTISE:
-                    showMessage("Stop advertising");
-                    break;
-            }
-        }
-    };
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
index 925c766..a6489c1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
@@ -19,6 +19,7 @@
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
+import java.lang.Math;
 import java.util.Set;
 import java.util.Map;
 import java.util.HashMap;
@@ -29,6 +30,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
+import android.os.CountDownTimer;
 import android.util.Log;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -44,6 +46,10 @@
     private Map<Integer, Integer> mCount;
     private int[] mPowerLevel;
 
+    private TextView mTimerText;
+    private CountDownTimer mTimer;
+    private static final long REFRESH_MAC_TIME = 930000; // 15.5 min
+
     private static final int[] POWER_DBM = {-21, -15, -7, 1, 9};
 
     @Override
@@ -54,9 +60,25 @@
         setInfoResources(R.string.ble_power_level_name,
                          R.string.ble_power_level_info, -1);
 
-        mCount = new HashMap<Integer, Integer>();
+        mTimerText = (TextView)findViewById(R.id.ble_timer);
+        mTimer = new CountDownTimer(REFRESH_MAC_TIME, 1000) {
+            @Override
+            public void onTick(long millis) {
+                int min = (int)millis / 60000;
+                int sec = ((int)millis / 1000) % 60;
+                mTimerText.setText(min + ":" + sec);
+            }
+
+            @Override
+            public void onFinish() {
+                mTimerText.setTextColor(getResources().getColor(R.color.red));
+                mTimerText.setText("Time is up!");
+            }
+        };
+
         mRssiText = new HashMap<Integer, TextView>();
         mCountText = new HashMap<Integer, TextView>();
+        mCount = null;
         mMacText = new HashMap<Integer, TextView>();
         mSetPowerText = new HashMap<Integer, TextView>();
         mPowerLevel = new int[]{AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
@@ -64,9 +86,6 @@
             AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM,
             AdvertiseSettings.ADVERTISE_TX_POWER_HIGH};
 
-        for (int i : mPowerLevel) {
-            mCount.put(i, 0);
-        }
         mMacText.put(AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
             (TextView)findViewById(R.id.ble_ultra_low_mac));
         mMacText.put(AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
@@ -114,6 +133,7 @@
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(BleScannerService.BLE_POWER_LEVEL);
+        filter.addAction(BleScannerService.BLE_PRIVACY_NEW_MAC_RECEIVE);
         registerReceiver(onBroadcast, filter);
     }
 
@@ -135,26 +155,43 @@
             switch (intent.getAction()) {
                 case BleScannerService.BLE_POWER_LEVEL:
                     int powerLevelBit = intent.getIntExtra(
-                        BleScannerService.EXTRA_POWER_LEVEL_BIT, -1);
+                            BleScannerService.EXTRA_POWER_LEVEL_BIT, -1);
                     int powerLevel = intent.getIntExtra(BleScannerService.EXTRA_POWER_LEVEL, -2);
                     if (powerLevelBit < 0 || powerLevelBit > 3) {
-                      Toast.makeText(context, "Invalid power level", Toast.LENGTH_SHORT).show();
-                      break;
+                        Toast.makeText(context, "Invalid power level", Toast.LENGTH_SHORT).show();
+                        break;
+                    }
+
+                    if (mCount == null) {
+                        mCount = new HashMap<Integer, Integer>();
+                        for (int i : mPowerLevel) {
+                            mCount.put(i, 0);
+                        }
+                        mTimer.start();
                     }
                     Integer t = mCount.get(powerLevelBit) + 1;
                     mCount.put(powerLevelBit, t);
                     mCountText.get(powerLevelBit).setText(t.toString());
+
                     mMacText.get(powerLevelBit)
                         .setText(intent.getStringExtra(BleScannerService.EXTRA_MAC_ADDRESS));
                     mRssiText.get(powerLevelBit)
                         .setText(intent.getStringExtra(BleScannerService.EXTRA_RSSI));
-                    if (POWER_DBM[powerLevelBit] == powerLevel) {
+                    if (Math.abs(POWER_DBM[powerLevelBit] - powerLevel) < 2) {
                         mSetPowerText.get(powerLevelBit).setText("Valid power level");
                     } else {
                         mSetPowerText.get(powerLevelBit)
                             .setText("Unknown BLe advertise tx power: " + powerLevel);
                     }
                     break;
+                case BleScannerService.BLE_PRIVACY_NEW_MAC_RECEIVE:
+                     Toast.makeText(context, "New MAC address detected", Toast.LENGTH_SHORT)
+                            .show();
+                     mTimerText.setTextColor(getResources().getColor(R.color.green));
+                     mTimerText.append("   Get new MAC address.");
+                     mTimer.cancel();
+                     getPassButton().setEnabled(true);
+                     break;
             }
         }
     };
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPrivacyMacActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPrivacyMacActivity.java
deleted file mode 100644
index b7e4129..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPrivacyMacActivity.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * 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.bluetooth;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.CountDownTimer;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class BleScannerPrivacyMacActivity extends PassFailButtons.Activity {
-
-    private static final String TAG = "BleScannerPrivacyMac";
-
-    private int mMacCount;
-    private int mRespCount;
-    private TextView mMacText;
-    private TextView mCountText;
-    private TextView mRespText;
-    private TextView mTimerText;
-    private CountDownTimer mTimer;
-    private static final long REFRESH_MAC_TIME = 930000; // 15.5 min
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.ble_scanner_privacy_mac);
-        setPassFailButtonClickListeners();
-        setInfoResources(R.string.ble_privacy_mac_name,
-                         R.string.ble_privacy_mac_info, -1);
-        getPassButton().setEnabled(false);
-        mMacCount = 0;
-        mRespCount = 0;
-        mMacText = (TextView)findViewById(R.id.ble_mac_address);
-        mCountText = (TextView)findViewById(R.id.ble_mac_count);
-        mRespText = (TextView)findViewById(R.id.ble_resp_count);
-        mTimerText = (TextView)findViewById(R.id.ble_timer);
-
-        mTimer = new CountDownTimer(REFRESH_MAC_TIME, 1000) {
-            @Override
-            public void onTick(long millis) {
-                int min = (int)millis / 60000;
-                int sec = ((int)millis / 1000) % 60;
-                mTimerText.setText(min + ":" + sec);
-            }
-
-            @Override
-            public void onFinish() {
-                mTimerText.setTextColor(getResources().getColor(R.color.red));
-                mTimerText.setText("Time is up!");
-                stop();
-            }
-        };
-
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(BleScannerService.BLE_PRIVACY_NEW_MAC_RECEIVE);
-        filter.addAction(BleScannerService.BLE_MAC_ADDRESS);
-        filter.addAction(BleScannerService.BLE_SCAN_RESP);
-        registerReceiver(onBroadcast, filter);
-
-        if (mMacCount == 0) {
-            Intent intent = new Intent(this, BleScannerService.class);
-            intent.putExtra(BleScannerService.EXTRA_COMMAND, BleScannerService.COMMAND_PRIVACY_MAC);
-            startService(intent);
-        }
-    }
-
-
-    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            switch (intent.getAction()) {
-                case BleScannerService.BLE_PRIVACY_NEW_MAC_RECEIVE:
-                    mMacText.append(", " + intent.getStringExtra(BleScannerService.EXTRA_MAC_ADDRESS));
-                    Toast.makeText(context, "New MAC address detected", Toast.LENGTH_SHORT).show();
-                    mTimerText.setTextColor(getResources().getColor(R.color.green));
-                    mTimerText.append("   Get new MAC address.");
-                    mTimer.cancel();
-                    getPassButton().setEnabled(true);
-                    break;
-                case BleScannerService.BLE_MAC_ADDRESS:
-                    if (mMacCount == 0) {
-                        mMacText.setText(intent.getStringExtra(BleScannerService.EXTRA_MAC_ADDRESS));
-                        mTimer.start();
-                    }
-                    mMacCount++;
-                    mCountText.setText("Count: " + mMacCount);
-                    break;
-                case BleScannerService.BLE_SCAN_RESP:
-                    mRespCount++;
-                    mRespText.setText("Response: " + mRespCount);
-                    break;
-            }
-        }
-    };
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        unregisterReceiver(onBroadcast);
-    }
-
-    private void stop() {
-        stopService(new Intent(this, BleScannerService.class));
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        stop();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
index c8e7953..d3d96ac 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
@@ -19,6 +19,7 @@
 import android.app.Service;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.AdvertiseSettings;
 import android.bluetooth.le.BluetoothLeScanner;
 import android.bluetooth.le.ScanCallback;
 import android.bluetooth.le.ScanFilter;
@@ -44,7 +45,6 @@
     public static final boolean DEBUG = true;
     public static final String TAG = "BleScannerService";
 
-    public static final int COMMAND_PRIVACY_MAC = 0;
     public static final int COMMAND_POWER_LEVEL = 1;
     public static final int COMMAND_SCAN_WITH_FILTER = 2;
     public static final int COMMAND_SCAN_WITHOUT_FILTER = 3;
@@ -105,21 +105,6 @@
 
             int command = intent.getIntExtra(EXTRA_COMMAND, -1);
             switch (command) {
-                case COMMAND_PRIVACY_MAC:
-                    filters.add(new ScanFilter.Builder()
-                        .setManufacturerData(MANUFACTURER_TEST_ID,
-                            new byte[]{MANUFACTURER_TEST_ID, 0})
-                        .setServiceData(new ParcelUuid(BleAdvertiserService.PRIVACY_MAC_UUID),
-                            BleAdvertiserService.PRIVACY_MAC_DATA)
-                        .build());
-                    filters.add(new ScanFilter.Builder()
-                        .setManufacturerData(MANUFACTURER_TEST_ID,
-                            new byte[]{MANUFACTURER_TEST_ID, 0})
-                        .setServiceData(new ParcelUuid(BleAdvertiserService.SCAN_RESP_UUID),
-                            BleAdvertiserService.PRIVACY_RESPONSE)
-                        .build());
-                    settingBuilder.setScanMode(ScanSettings.SCAN_MODE_BALANCED);
-                    break;
                 case COMMAND_POWER_LEVEL:
                     filters.add(new ScanFilter.Builder()
                         .setManufacturerData(MANUFACTURER_TEST_ID,
@@ -182,21 +167,6 @@
             String mac = result.getDevice().getAddress();
             Map<ParcelUuid, byte[]> serviceData = record.getServiceData();
 
-            if (serviceData.get(new ParcelUuid(BleAdvertiserService.PRIVACY_MAC_UUID)) != null) {
-                Intent privacyIntent = new Intent(BLE_MAC_ADDRESS);
-                privacyIntent.putExtra(EXTRA_MAC_ADDRESS, mac);
-                sendBroadcast(privacyIntent);
-
-                if (mOldMac == null) {
-                    mOldMac = mac;
-                } else if (!mOldMac.equals(mac)) {
-                    mOldMac = mac;
-                    Intent newIntent = new Intent(BLE_PRIVACY_NEW_MAC_RECEIVE);
-                    newIntent.putExtra(EXTRA_MAC_ADDRESS, mac);
-                    sendBroadcast(newIntent);
-                }
-            }
-
             if (serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID)) != null) {
                 byte[] data =
                         serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID));
@@ -207,6 +177,18 @@
                     powerIntent.putExtra(EXTRA_RSSI, new Integer(result.getRssi()).toString());
                     powerIntent.putExtra(EXTRA_POWER_LEVEL_BIT, (int)data[2]);
                     sendBroadcast(powerIntent);
+
+                    // Check privacy mac.
+                    if (data[2] == AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) {
+                        String newMac = result.getDevice().getAddress();
+                        if (mOldMac == null) {
+                            mOldMac = newMac;
+                        } else if (!mOldMac.equals(mac)) {
+                            mOldMac = newMac;
+                            Intent newIntent = new Intent(BLE_PRIVACY_NEW_MAC_RECEIVE);
+                            sendBroadcast(newIntent);
+                        }
+                    }
                 }
             }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
index f19142f..d325b65 100755
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
@@ -18,7 +18,6 @@
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
-import android.app.Activity;
 import android.app.AlertDialog;
 import android.graphics.Bitmap;
 import android.graphics.Color;
@@ -47,9 +46,7 @@
 import android.widget.Toast;
 
 import java.io.IOException;
-import java.lang.InterruptedException;
 import java.lang.Math;
-import java.lang.Thread;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Comparator;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java
index d71dca2..178a811 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java
@@ -69,7 +69,7 @@
     private List<SelectableResolution> mSupportedResolutions;
     private ArrayAdapter<SelectableResolution> mAdapter;
 
-    private int mCameraId;
+    private SelectableResolution mSelectedResolution;
     private Camera mCamera;
     private Size mSurfaceSize;
     private boolean mCameraInitialized = false;
@@ -165,12 +165,7 @@
                     AdapterView<?> parent, View view, int position, long id) {
                 if (mSupportedResolutions != null) {
                     SelectableResolution resolution = mSupportedResolutions.get(position);
-
-                    switchToCamera(resolution.cameraId, false);
-
-                    Camera.Parameters params = mCamera.getParameters();
-                    params.setPictureSize(resolution.width, resolution.height);
-                    mCamera.setParameters(params);
+                    switchToCamera(resolution, false);
 
                     // It should be guaranteed that the FOV is correctly updated after setParameters().
                     mReportedFovPrePictureTaken = mCamera.getParameters().getHorizontalViewAngle();
@@ -376,7 +371,7 @@
                     public void onCancel(DialogInterface arg0) {
                         // User cancelled preview size selection.
                         mPreviewSizes = null;
-                        switchToCamera(mCameraId, true);
+                        switchToCamera(mSelectedResolution, true);
                     }
                 }).
                 setSingleChoiceItems(choices, 0, new DialogInterface.OnClickListener() {
@@ -389,7 +384,7 @@
 
                         if (mPreviewSizeCamerasToProcess.isEmpty()) {
                             // We're done, re-initialize camera.
-                            switchToCamera(mCameraId, true);
+                            switchToCamera(mSelectedResolution, true);
                         } else {
                             // Process other cameras.
                             showNextDialogToChoosePreviewSize();
@@ -415,7 +410,7 @@
 
         // Either use chosen preview size for current camera or automatically
         // choose preview size based on view dimensions.
-        Size selectedPreviewSize = (mPreviewSizes != null) ? mPreviewSizes[mCameraId] :
+        Size selectedPreviewSize = (mPreviewSizes != null) ? mPreviewSizes[mSelectedResolution.cameraId] :
             getBestPreviewSize(mSurfaceSize.width, mSurfaceSize.height, params);
         if (selectedPreviewSize != null) {
             params.setPreviewSize(selectedPreviewSize.width, selectedPreviewSize.height);
@@ -427,19 +422,20 @@
 
     private void startPreview() {
         if (mCameraInitialized && mCamera != null) {
-            setCameraDisplayOrientation(this, mCameraId, mCamera);
+            setCameraDisplayOrientation(this, mSelectedResolution.cameraId, mCamera);
             mCamera.startPreview();
             mPreviewActive = true;
         }
     }
 
-    private void switchToCamera(int cameraId, boolean initializeCamera) {
+    private void switchToCamera(SelectableResolution resolution, boolean initializeCamera) {
         if (mCamera != null) {
             mCamera.stopPreview();
             mCamera.release();
         }
-        mCameraId = cameraId;
-        mCamera = Camera.open(cameraId);
+
+        mSelectedResolution = resolution;
+        mCamera = Camera.open(mSelectedResolution.cameraId);
 
         if (initializeCamera){
           initializeCamera();
@@ -472,7 +468,7 @@
      * Set the common camera parameters on the given camera and returns the
      * parameter object for further modification, if needed.
      */
-    private static Camera.Parameters setCameraParams(Camera camera) {
+    private Camera.Parameters setCameraParams(Camera camera) {
         // The picture size is taken and set from the spinner selection
         // callback.
         Camera.Parameters params = camera.getParameters();
@@ -480,6 +476,7 @@
         params.setJpegQuality(100);
         params.setFocusMode(getFocusMode(camera));
         params.setZoom(0);
+        params.setPictureSize(mSelectedResolution.width, mSelectedResolution.height);
         return params;
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/intents/CameraIntentsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/intents/CameraIntentsActivity.java
index 6881bea..9204de3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/intents/CameraIntentsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/intents/CameraIntentsActivity.java
@@ -19,32 +19,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.ImageFormat;
-import android.graphics.Matrix;
 import android.hardware.Camera;
 import android.os.Bundle;
-import android.os.Handler;
 import android.util.Log;
 import android.view.SurfaceHolder;
-import android.view.SurfaceView;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
 import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout.LayoutParams;
 import android.widget.TextView;
 
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.TestResult;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
 import java.util.TreeSet;
 
 /**
diff --git a/hostsidetests/appsecurity/certs/keysets/README b/hostsidetests/appsecurity/certs/keysets/README
new file mode 100644
index 0000000..251dab1
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/README
@@ -0,0 +1,9 @@
+# Generated with:
+development/tools/make_key cts-keyset-test-a         '/CN=unit_test_a'
+development/tools/make_key cts-keyset-test-b         '/CN=unit_test_b'
+development/tools/make_key cts-keyset-test-c         '/CN=unit_test_c'
+
+# Display public key (for use in Manifest) with:
+openssl x509 -in cts-keyset-test-a.x509.pem -inform PEM -pubkey
+openssl x509 -in cts-keyset-test-b.x509.pem -inform PEM -pubkey
+openssl x509 -in cts-keyset-test-c.x509.pem -inform PEM -pubkey
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a.pk8 b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a.pk8
new file mode 100644
index 0000000..b0dd6a3
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a.pk8
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a.x509.pem b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a.x509.pem
new file mode 100644
index 0000000..cee8227
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a.x509.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCzCCAfOgAwIBAgIJAJ126KYAFwgTMA0GCSqGSIb3DQEBBQUAMBwxGjAYBgNV
+BAMMEWN0cy1rZXlzZXQtdGVzdC1hMB4XDTE0MDkxMTAwNDQ0OFoXDTQyMDEyNzAw
+NDQ0OFowHDEaMBgGA1UEAwwRY3RzLWtleXNldC10ZXN0LWEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDB/nMluW9hIHtibuiv/saCAAC7uantGvKQ8mxe
+Gh3x2gWFVPmzt4XcDgwITnm+8A0/se/AzDZv5PqrHs+rRUm1ttIO2UEcG0hzjs+O
+rQKwODn3QFRyAqns90n0npRWC3MOdXpwYSleZJqDexj6WqJbTjK0+EJXDNhNYZ1h
+735MiXjtwGu95F8s6Uaty4VB77MJOYMWrMEoJEcr1vuXk8Na9dfKDrlS78wFQD9N
+lY7R8So6XFkb+efoNQpAuE92YlFdYndaow0yEkYP6cq2SZ1fvTfFGqaDiH7qDRLs
+z1jchDY1QbLDTkBjMKC4cH8y/q5UiJbrn3ClvJvjlOAobdSFAgMBAAGjUDBOMB0G
+A1UdDgQWBBTev2AuCLdXO85IFqwy6rIV+wUokjAfBgNVHSMEGDAWgBTev2AuCLdX
+O85IFqwy6rIV+wUokjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCS
+8bjQglLYCNMFHc6AeAvSfu/j9rbZNTmK+0SCCUYbb4s1LoMNQ1hmHhs+nrmrOTe9
+3VgaKPUz2h6+toOM5KhMpkxDUHxe+VKJF4V+TRxMWZbPaz0wgj21FKcV7u5wnWnj
+i08O9dzksIzkD9UrOaxlExG20YFJE9kizoR0i2mZJWhR+1g6SeNc7PeaUnEI344G
+LfSDGt27EqZhmZ1BhJ4lRRUMq3TJFEfdFeVc3z+AgtyrZnxc7jNQ0PFdOXDtzz6B
+iC6AmFsMC/mRettVxjTeOpLo+12UE7FwO+wRa57pNGtljzlKz+DGBAZxi+gLcRDf
+i0TJhPAB4dFqrDgxr+4Y
+-----END CERTIFICATE-----
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b.pk8 b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b.pk8
new file mode 100644
index 0000000..bf6dee1
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b.pk8
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b.x509.pem b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b.x509.pem
new file mode 100644
index 0000000..912861f
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b.x509.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCzCCAfOgAwIBAgIJAOZwpbLsHooSMA0GCSqGSIb3DQEBBQUAMBwxGjAYBgNV
+BAMMEWN0cy1rZXlzZXQtdGVzdC1iMB4XDTE0MDkxMTAwNDQ0MVoXDTQyMDEyNzAw
+NDQ0MVowHDEaMBgGA1UEAwwRY3RzLWtleXNldC10ZXN0LWIwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCh4VmoypNtmKjMVNcyRe1IolHOfao4NmC9VcAD
+ApOLnTFhxs9wdN8rG2J/z6rs4Kn/nQlgMffZuDrCRS6efn50RoeTFljx3u7Djq1C
+2Xl00aL7pxzgx7NUsJLqeSo0O6wCB2+AtToWXpIaLTYpOnW+S3oLAs73vtgk/uS3
+2i4NFMbsBImKrc7JFGg6pgeEP2CmxtSrjD7VtcZ+65m6MDV1fKi9e2+sdQY50UgQ
+Fg5VgZ8JzCHeVc+aM0kyUe0pCS6urz8sftrUHmhyhcIazJHxgd2VZ+upEB/OA4HU
+oKc02ZaqyRT0s5yLe5Cf0gN4wQWYB3wWoXxLBX7gu52T/FYZAgMBAAGjUDBOMB0G
+A1UdDgQWBBTM1NnUfcwYiJ3Loy3jfmVwyI+BCTAfBgNVHSMEGDAWgBTM1NnUfcwY
+iJ3Loy3jfmVwyI+BCTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBh
+lRz5yaYpswtWDVPWKnJ5btyXsLIQtWeFkxGxRXSrsFLvCMq7CxjO9VF1l+q+6UmK
+B6BEcrjm7uhmjAXS/ygUGjY1FZNVHwydJ/60Nn/Q0jx242A1+dBtLSS0FnEg+r3P
+3fvocr0SemAt6FY61gJ+4Zr8IQZc8C1qr5e/eDiMPBKectGzH1cniWqq1/5nc/vC
+hTTokZSnXh7PZLzF+iKOceO+nvx4yzm7q/YOM0tAP8PknrWcNAeIPRDvsERwp9fR
+IRTnyd3Ds4H/xD6OioMO+lk45H7vDU3TmoAYbDtCNvgS9Sd1lB/h3XPVH29QqwkW
+4xScMf6rzziGC+RdETpU
+-----END CERTIFICATE-----
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-c.pk8 b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-c.pk8
new file mode 100644
index 0000000..303f1ad
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-c.pk8
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-c.x509.pem b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-c.x509.pem
new file mode 100644
index 0000000..324f218
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-c.x509.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCzCCAfOgAwIBAgIJAI8ugk5OF4ENMA0GCSqGSIb3DQEBBQUAMBwxGjAYBgNV
+BAMMEWN0cy1rZXlzZXQtdGVzdC1jMB4XDTE0MDkxMTAwNDQyMloXDTQyMDEyNzAw
+NDQyMlowHDEaMBgGA1UEAwwRY3RzLWtleXNldC10ZXN0LWMwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCvAgn+n3NnqkZ7uHWUElQsTmVthsLeaHkbjc6w
+n4HQJ5s3grMQrJWD7CaS4ZK8bbFdSnUGVuKlKOdMnltS3aIG7AHzUu+6aD0Y58Kl
+MEa18ThriKC1+jt7ZTwhtHMRhuFpmUESYLUENS91MV0xEZk+6FRwyTCK3hGkeQvq
+u22459p6gnCyASNsQvLOByb7Vnj0N6f8maZc0YzDX9AyJsEUa8aSG7aseD9JiIqm
+6lyVTgUh4Atw5Kc+Sutjou5IBMcOdi+68rdWG7QQEogP6sC/mPoE2+e7blIB/caB
+Ls8u7JWWGITmneFN69efmD/u2MmVdrQWxsyWcV/ndbI/2lFbAgMBAAGjUDBOMB0G
+A1UdDgQWBBQbgTfHOXShdjNob5N5in97g4W97TAfBgNVHSMEGDAWgBQbgTfHOXSh
+djNob5N5in97g4W97TAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAn
+bOLKe3ixKqLkMsgocHWvkeUqFahYbiPN11JKTFrgQVYwfpUnXN/YQfLSjAWDyzZ3
+niXYSai2COtIqEpQICp4JceEfoZUCbHdATA7Wxvfr+yrv+HG7F8wzhyxa5Pbcu9y
+b3ekjKT1rF4SxK0Ixt9vv34VSO98qAzx2Yq7VQwOKLJG6MDxqXX/tiTxpK7sEfAb
+pgJjHVZkX1rgQtv2e0RLFgcRyiYpxFbFzBLi/1b6EzK2kkg9FNLm+44CYkYFj7WC
+bjlY7o94DQ/CuEDVHCu/DSTp4QjvHC2ewTeXu05XkzSWKKLdsqecnZxXNueuqT5F
+Uhj9Fi4KQqT7tKqd+CuK
+-----END CERTIFICATE-----
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/DocumentsTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/DocumentsTest.java
new file mode 100644
index 0000000..fbde558
--- /dev/null
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/DocumentsTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.appsecurity;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+public class DocumentsTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+    private static final String PROVIDER_PKG = "com.android.cts.documentprovider";
+    private static final String PROVIDER_APK = "CtsDocumentProvider.apk";
+
+    private static final String CLIENT_PKG = "com.android.cts.documentclient";
+    private static final String CLIENT_APK = "CtsDocumentClient.apk";
+
+    private IAbi mAbi;
+    private CtsBuildHelper mCtsBuild;
+
+    @Override
+    public void setAbi(IAbi abi) {
+        mAbi = abi;
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        assertNotNull(mAbi);
+        assertNotNull(mCtsBuild);
+
+        getDevice().uninstallPackage(PROVIDER_PKG);
+        getDevice().uninstallPackage(CLIENT_PKG);
+
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(PROVIDER_APK), false));
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(CLIENT_APK), false));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        getDevice().uninstallPackage(PROVIDER_PKG);
+        getDevice().uninstallPackage(CLIENT_PKG);
+    }
+
+    public void testOpenSimple() throws Exception {
+        runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testOpenSimple");
+    }
+
+    public void testCreateNew() throws Exception {
+        runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testCreateNew");
+    }
+
+    public void testCreateExisting() throws Exception {
+        runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testCreateExisting");
+    }
+
+    public void testTree() throws Exception {
+        runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testTree");
+    }
+
+    public void testGetContent() throws Exception {
+        runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testGetContent");
+    }
+
+    public void runDeviceTests(String packageName, String testClassName, String testMethodName)
+            throws DeviceNotAvailableException {
+        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
+    }
+}
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java
new file mode 100644
index 0000000..dae5ee7
--- /dev/null
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java
@@ -0,0 +1,435 @@
+/*
+ * 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 android.cts.appsecurity;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Map;
+
+/**
+ * Tests for Keyset based features.
+ */
+public class KeySetHostTest extends DeviceTestCase implements IBuildReceiver {
+
+    private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
+
+    /* package with device-side tests */
+    private static final String KEYSET_TEST_PKG = "com.android.cts.keysets.testapp";
+    private static final String KEYSET_TEST_APP_APK = "CtsKeySetTestApp.apk";
+
+    /* plain test apks with different signing and upgrade keysets */
+    private static final String KEYSET_PKG = "com.android.cts.keysets";
+    private static final String A_SIGNED_NO_UPGRADE =
+            "CtsKeySetSigningAUpgradeNone.apk";
+    private static final String A_SIGNED_A_UPGRADE =
+            "CtsKeySetSigningAUpgradeA.apk";
+    private static final String A_SIGNED_B_UPGRADE =
+            "CtsKeySetSigningAUpgradeB.apk";
+    private static final String A_SIGNED_A_OR_B_UPGRADE =
+            "CtsKeySetSigningAUpgradeAOrB.apk";
+    private static final String B_SIGNED_A_UPGRADE =
+            "CtsKeySetSigningBUpgradeA.apk";
+    private static final String B_SIGNED_B_UPGRADE =
+            "CtsKeySetSigningBUpgradeB.apk";
+    private static final String A_AND_B_SIGNED_A_UPGRADE =
+            "CtsKeySetSigningAAndBUpgradeA.apk";
+    private static final String A_AND_B_SIGNED_B_UPGRADE =
+            "CtsKeySetSigningAAndBUpgradeB.apk";
+    private static final String A_AND_C_SIGNED_B_UPGRADE =
+            "CtsKeySetSigningAAndCUpgradeB.apk";
+
+    /* package which defines the KEYSET_PERM_NAME signature permission */
+    private static final String KEYSET_PERM_DEF_PKG =
+            "com.android.cts.keysets_permdef";
+
+    /* The apks defining and using the permission have both A and B as upgrade keys */
+    private static final String PERM_DEF_A_SIGNED =
+            "CtsKeySetPermDefSigningA.apk";
+    private static final String PERM_DEF_B_SIGNED =
+            "CtsKeySetPermDefSigningB.apk";
+    private static final String PERM_USE_A_SIGNED =
+            "CtsKeySetPermUseSigningA.apk";
+    private static final String PERM_USE_B_SIGNED =
+            "CtsKeySetPermUseSigningB.apk";
+
+    private static final String PERM_TEST_CLASS =
+        "com.android.cts.keysets.KeySetPermissionsTest";
+
+    private static final String LOG_TAG = "AppsecurityHostTests";
+
+    private File getTestAppFile(String fileName) throws FileNotFoundException {
+        return mCtsBuild.getTestApp(fileName);
+    }
+
+    /**
+     * Helper method that checks that all tests in given result passed, and attempts to generate
+     * a meaningful error message if they failed.
+     *
+     * @param result
+     */
+    private void assertDeviceTestsPass(TestRunResult result) {
+        assertFalse(String.format("Failed to successfully run device tests for %s. Reason: %s",
+                result.getName(), result.getRunFailureMessage()), result.isRunFailure());
+
+        if (result.hasFailedTests()) {
+
+            /* build a meaningful error message */
+            StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+            for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+                result.getTestResults().entrySet()) {
+                if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+                    errorBuilder.append(resultEntry.getKey().toString());
+                    errorBuilder.append(":\n");
+                    errorBuilder.append(resultEntry.getValue().getStackTrace());
+                }
+            }
+            fail(errorBuilder.toString());
+        }
+    }
+
+    /**
+     * Helper method that checks that all tests in given result passed, and attempts to generate
+     * a meaningful error message if they failed.
+     *
+     * @param result
+     */
+    private void assertDeviceTestsFail(String msg, TestRunResult result) {
+        assertFalse(String.format("Failed to successfully run device tests for %s. Reason: %s",
+                result.getName(), result.getRunFailureMessage()), result.isRunFailure());
+
+        if (!result.hasFailedTests()) {
+            fail(msg);
+        }
+    }
+
+    /**
+     * Helper method that will run the specified packages tests on device.
+     *
+     * @param pkgName Android application package for tests
+     * @return <code>true</code> if all tests passed.
+     * @throws DeviceNotAvailableException if connection to device was lost.
+     */
+    private boolean runDeviceTests(String pkgName) throws DeviceNotAvailableException {
+        return runDeviceTests(pkgName, null, null);
+    }
+
+    /**
+     * Helper method that will run the specified packages tests on device.
+     *
+     * @param pkgName Android application package for tests
+     * @return <code>true</code> if all tests passed.
+     * @throws DeviceNotAvailableException if connection to device was lost.
+     */
+    private boolean runDeviceTests(String pkgName, String testClassName, String testMethodName)
+            throws DeviceNotAvailableException {
+        TestRunResult runResult = doRunTests(pkgName, testClassName, testMethodName);
+        return !runResult.hasFailedTests();
+    }
+
+    /**
+     * Helper method to run tests and return the listener that collected the results.
+     *
+     * @param pkgName Android application package for tests
+     * @return the {@link TestRunResult}
+     * @throws DeviceNotAvailableException if connection to device was lost.
+     */
+    private TestRunResult doRunTests(String pkgName, String testClassName,
+            String testMethodName) throws DeviceNotAvailableException {
+
+        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName,
+                RUNNER, getDevice().getIDevice());
+        if (testClassName != null && testMethodName != null) {
+            testRunner.setMethodName(testClassName, testMethodName);
+        }
+        CollectingTestListener listener = new CollectingTestListener();
+        getDevice().runInstrumentationTests(testRunner, listener);
+        return listener.getCurrentRunResults();
+    }
+
+    /**
+     * Helper method which installs a package and an upgrade to it.
+     *
+     * @param pkgName - package name of apk.
+     * @param firstApk - first apk to install
+     * @param secondApk - apk to which we attempt to upgrade
+     * @param expectedResult - null if successful, otherwise expected error.
+     */
+    private String testPackageUpgrade(String pkgName, String firstApk,
+             String secondApk) throws Exception {
+        String installResult;
+        try {
+
+            /* cleanup test apps that might be installed from previous partial test run */
+            mDevice.uninstallPackage(pkgName);
+
+            installResult = mDevice.installPackage(getTestAppFile(firstApk),
+                    false);
+            /* we should always succeed on first-install */
+            assertNull(String.format("failed to install %s, Reason: %s", pkgName,
+                       installResult), installResult);
+
+            /* attempt to install upgrade */
+            installResult = mDevice.installPackage(getTestAppFile(secondApk),
+                    true);
+        } finally {
+            mDevice.uninstallPackage(pkgName);
+        }
+        return installResult;
+    }
+    /**
+     * A reference to the device under test.
+     */
+    private ITestDevice mDevice;
+
+    private CtsBuildHelper mCtsBuild;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    @Override
+        protected void setUp() throws Exception {
+        super.setUp();
+        mDevice = getDevice();
+        assertNotNull(mCtsBuild);
+    }
+
+    /**
+     * Tests for KeySet based key rotation
+     */
+
+    /*
+     * Check if an apk which does not specify an upgrade-key-set may be upgraded
+     * to an apk which does.
+     */
+    public void testNoKSToUpgradeKS() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_NO_UPGRADE, A_SIGNED_A_UPGRADE);
+        assertNull(String.format("failed to upgrade keyset app from no specified upgrade-key-set"
+                + "to version with specified upgrade-key-set, Reason: %s", installResult),
+                installResult);
+    }
+
+    /*
+     * Check if an apk which does specify an upgrade-key-set may be upgraded
+     * to an apk which does not.
+     */
+    public void testUpgradeKSToNoKS() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_A_UPGRADE, A_SIGNED_NO_UPGRADE);
+        assertNull(String.format("failed to upgrade keyset app from specified upgrade-key-set"
+                + "to version without specified upgrade-key-set, Reason: %s", installResult),
+                installResult);
+    }
+
+    /*
+     * Check if an apk signed by a key other than the upgrade keyset can update
+     * an app
+     */
+    public void testUpgradeKSWithWrongKey() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_A_UPGRADE, B_SIGNED_A_UPGRADE);
+        assertNotNull("upgrade to improperly signed app succeeded!", installResult);
+    }
+
+    /*
+     * Check if an apk signed by its signing key, which is not an upgrade key,
+     * can upgrade an app.
+     */
+    public void testUpgradeKSWithWrongSigningKey() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_B_UPGRADE, A_SIGNED_B_UPGRADE);
+         assertNotNull("upgrade to improperly signed app succeeded!",
+                 installResult);
+    }
+
+    /*
+     * Check if an apk signed by its upgrade key, which is not its signing key,
+     * can upgrade an app.
+     */
+    public void testUpgradeKSWithUpgradeKey() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_B_UPGRADE, B_SIGNED_B_UPGRADE);
+        assertNull(String.format("failed to upgrade keyset app from one signed by key-a"
+                 + "to version signed by upgrade-key-set key-b, Reason: %s", installResult),
+                 installResult);
+    }
+
+    /*
+     * Check if an apk signed by its upgrade key, which is its signing key, can
+     * upgrade an app.
+     */
+    public void testUpgradeKSWithSigningUpgradeKey() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_A_UPGRADE, A_SIGNED_A_UPGRADE);
+        assertNull(String.format("failed to upgrade keyset app from one signed by key-a"
+                    + "to version signed by upgrade-key-set key-b, Reason: %s", installResult),
+                    installResult);
+    }
+
+    /*
+     * Check if an apk signed by multiple keys, one of which is its upgrade key,
+     * can upgrade an app.
+     */
+    public void testMultipleUpgradeKSWithUpgradeKey() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_A_UPGRADE,
+                A_AND_B_SIGNED_A_UPGRADE);
+        assertNull(String.format("failed to upgrade keyset app from one signed by key-a"
+                + "to version signed by upgrade-key-set key-b, Reason: %s", installResult),
+                installResult);
+    }
+
+    /*
+     * Check if an apk signed by multiple keys, its signing keys,
+     * but none of which is an upgrade key, can upgrade an app.
+     */
+    public void testMultipleUpgradeKSWithSigningKey() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_AND_C_SIGNED_B_UPGRADE,
+                A_AND_C_SIGNED_B_UPGRADE);
+        assertNotNull("upgrade to improperly signed app succeeded!", installResult);
+    }
+
+    /*
+     * Check if an apk which defines multiple (two) upgrade keysets is
+     * upgrade-able by either.
+     */
+    public void testUpgradeKSWithMultipleUpgradeKeySetsFirstKey() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_A_OR_B_UPGRADE,
+                A_SIGNED_A_UPGRADE);
+        assertNull(String.format("failed to upgrade keyset app from one signed by key-a"
+                + "to one signed by first upgrade keyset key-a, Reason: %s", installResult),
+                installResult);
+        installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_A_OR_B_UPGRADE,
+                B_SIGNED_B_UPGRADE);
+        assertNull(String.format("failed to upgrade keyset app from one signed by key-a"
+                + "to one signed by second upgrade keyset key-b, Reason: %s", installResult),
+                installResult);
+    }
+
+    /**
+     * Helper method which installs a package defining a permission and a package
+     * using the permission, and then rotates the signing keys for one of them.
+     * A device-side test is then used to ascertain whether or not the permission
+     * was appropriately gained or lost.
+     *
+     * @param permDefApk - apk to install which defines the sig-permissoin
+     * @param permUseApk - apk to install which declares it uses the permission
+     * @param upgradeApk - apk to install which upgrades one of the first two
+     * @param hasPermBeforeUpgrade - whether we expect the consuming app to have
+     *        the permission before the upgrade takes place.
+     * @param hasPermAfterUpgrade - whether we expect the consuming app to have
+     *        the permission after the upgrade takes place.
+     */
+    private void testKeyRotationPerm(String permDefApk, String permUseApk,
+            String upgradeApk, boolean hasPermBeforeUpgrade,
+            boolean hasPermAfterUpgrade) throws Exception {
+        try {
+
+            /* cleanup test apps that might be installed from previous partial test run */
+            mDevice.uninstallPackage(KEYSET_PKG);
+            mDevice.uninstallPackage(KEYSET_PERM_DEF_PKG);
+            mDevice.uninstallPackage(KEYSET_TEST_PKG);
+
+            /* install PERM_DEF, KEYSET_APP and KEYSET_TEST_APP */
+            String installResult = mDevice.installPackage(
+                    getTestAppFile(permDefApk), false);
+            assertNull(String.format("failed to install keyset perm-def app, Reason: %s",
+                       installResult), installResult);
+            installResult = getDevice().installPackage(
+                    getTestAppFile(permUseApk), false);
+            assertNull(String.format("failed to install keyset test app. Reason: %s",
+                    installResult), installResult);
+            installResult = getDevice().installPackage(
+                    getTestAppFile(KEYSET_TEST_APP_APK), false);
+            assertNull(String.format("failed to install keyset test app. Reason: %s",
+                    installResult), installResult);
+
+            /* verify package does have perm */
+            TestRunResult result = doRunTests(KEYSET_TEST_PKG, PERM_TEST_CLASS,
+                    "testHasPerm");
+            if (hasPermBeforeUpgrade) {
+                assertDeviceTestsPass(result);
+            } else {
+                assertDeviceTestsFail(" has permission permission it should not have.", result);
+            }
+
+            /* rotate keys */
+            installResult = mDevice.installPackage(getTestAppFile(upgradeApk),
+                    true);
+            result = doRunTests(KEYSET_TEST_PKG, PERM_TEST_CLASS,
+                    "testHasPerm");
+            if (hasPermAfterUpgrade) {
+                assertDeviceTestsPass(result);
+            } else {
+                assertDeviceTestsFail(KEYSET_PKG + " has permission it should not have.", result);
+            }
+        } finally {
+            mDevice.uninstallPackage(KEYSET_PKG);
+            mDevice.uninstallPackage(KEYSET_PERM_DEF_PKG);
+            mDevice.uninstallPackage(KEYSET_TEST_PKG);
+        }
+    }
+
+    /*
+     * Check if an apk gains signature-level permission after changing to a new
+     * signature, for which a permission should be granted.
+     */
+    public void testUpgradeSigPermGained() throws Exception {
+        testKeyRotationPerm(PERM_DEF_A_SIGNED, PERM_USE_B_SIGNED, PERM_USE_A_SIGNED,
+                false, true);
+    }
+
+    /*
+     * Check if an apk loses signature-level permission after changing to a new
+     * signature, from one for which a permission was previously granted.
+     */
+    public void testUpgradeSigPermLost() throws Exception {
+        testKeyRotationPerm(PERM_DEF_A_SIGNED, PERM_USE_A_SIGNED, PERM_USE_B_SIGNED,
+                true, false);
+    }
+
+    /*
+     * Check if an apk gains signature-level permission after the app defining
+     * it rotates to the same signature.
+     */
+    public void testUpgradeDefinerSigPermGained() throws Exception {
+        testKeyRotationPerm(PERM_DEF_A_SIGNED, PERM_USE_B_SIGNED, PERM_DEF_B_SIGNED,
+                false, true);
+    }
+
+    /*
+     * Check if an apk loses signature-level permission after the app defining
+     * it rotates to a different signature.
+     */
+    public void testUpgradeDefinerSigPermLost() throws Exception {
+        testKeyRotationPerm(PERM_DEF_A_SIGNED, PERM_USE_A_SIGNED, PERM_DEF_B_SIGNED,
+                true, false);
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
index b9bc768..264c0b1 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
@@ -18,15 +18,9 @@
 
 import com.android.cts.tradefed.build.CtsBuildHelper;
 import com.android.cts.util.AbiUtils;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.ddmlib.testrunner.TestResult;
-import com.android.ddmlib.testrunner.TestResult.TestStatus;
-import com.android.ddmlib.testrunner.TestRunResult;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.result.CollectingTestListener;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IAbiReceiver;
@@ -37,7 +31,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Tests that verify installing of various split APKs from host side.
@@ -355,43 +348,8 @@
         }
     }
 
-    public void runDeviceTests(String packageName) throws DeviceNotAvailableException {
-        runDeviceTests(packageName, null, null);
-    }
-
     public void runDeviceTests(String packageName, String testClassName, String testMethodName)
             throws DeviceNotAvailableException {
-        if (testClassName != null && testClassName.startsWith(".")) {
-            testClassName = packageName + testClassName;
-        }
-
-        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
-                "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
-        if (testClassName != null && testMethodName != null) {
-            testRunner.setMethodName(testClassName, testMethodName);
-        }
-
-        final CollectingTestListener listener = new CollectingTestListener();
-        getDevice().runInstrumentationTests(testRunner, listener);
-
-        final TestRunResult result = listener.getCurrentRunResults();
-        if (result.isRunFailure()) {
-            fail("Failed to successfully run device tests for " + result.getName() + ": "
-                    + result.getRunFailureMessage());
-        }
-
-        if (result.hasFailedTests()) {
-            // build a meaningful error message
-            StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
-            for (Map.Entry<TestIdentifier, TestResult> resultEntry :
-                result.getTestResults().entrySet()) {
-                if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
-                    errorBuilder.append(resultEntry.getKey().toString());
-                    errorBuilder.append(":\n");
-                    errorBuilder.append(resultEntry.getValue().getStackTrace());
-                }
-            }
-            fail(errorBuilder.toString());
-        }
+        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
     }
 }
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/Utils.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/Utils.java
new file mode 100644
index 0000000..c58d6bf
--- /dev/null
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/Utils.java
@@ -0,0 +1,71 @@
+/*
+ * 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.appsecurity;
+
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.CollectingTestListener;
+
+import java.util.Map;
+
+public class Utils {
+    public static void runDeviceTests(ITestDevice device, String packageName)
+            throws DeviceNotAvailableException {
+        runDeviceTests(device, packageName, null, null);
+    }
+
+    public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
+            String testMethodName) throws DeviceNotAvailableException {
+        if (testClassName != null && testClassName.startsWith(".")) {
+            testClassName = packageName + testClassName;
+        }
+
+        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
+                "android.support.test.runner.AndroidJUnitRunner", device.getIDevice());
+        if (testClassName != null && testMethodName != null) {
+            testRunner.setMethodName(testClassName, testMethodName);
+        }
+
+        final CollectingTestListener listener = new CollectingTestListener();
+        device.runInstrumentationTests(testRunner, listener);
+
+        final TestRunResult result = listener.getCurrentRunResults();
+        if (result.isRunFailure()) {
+            throw new AssertionError("Failed to successfully run device tests for "
+                    + result.getName() + ": " + result.getRunFailureMessage());
+        }
+
+        if (result.hasFailedTests()) {
+            // build a meaningful error message
+            StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+            for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+                result.getTestResults().entrySet()) {
+                if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+                    errorBuilder.append(resultEntry.getKey().toString());
+                    errorBuilder.append(":\n");
+                    errorBuilder.append(resultEntry.getValue().getStackTrace());
+                }
+            }
+            throw new AssertionError(errorBuilder.toString());
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.mk b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.mk
new file mode 100644
index 0000000..910e3cd
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.mk
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ctsdeviceutil ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsDocumentClient
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/DocumentClient/AndroidManifest.xml
new file mode 100644
index 0000000..0064e15
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.documentclient">
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".MyActivity" />
+    </application>
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.documentclient" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
new file mode 100644
index 0000000..83187c7
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
@@ -0,0 +1,300 @@
+/*
+ * 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.documentclient;
+
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsProvider;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
+import android.test.InstrumentationTestCase;
+import android.test.MoreAsserts;
+
+import com.android.cts.documentclient.MyActivity.Result;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Tests for {@link DocumentsProvider} and interaction with platform intents
+ * like {@link Intent#ACTION_OPEN_DOCUMENT}.
+ */
+public class DocumentsClientTest extends InstrumentationTestCase {
+    private UiDevice mDevice;
+    private MyActivity mActivity;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
+                MyActivity.class, null);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        mActivity.finish();
+    }
+
+    public void testOpenSimple() throws Exception {
+        if (!supportedHardware()) return;
+
+        try {
+            // Opening without permission should fail
+            readFully(Uri.parse("content://com.android.cts.documentprovider/document/doc:file1"));
+            fail("Able to read data before opened!");
+        } catch (SecurityException expected) {
+        }
+
+        final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+        intent.addCategory(Intent.CATEGORY_OPENABLE);
+        intent.setType("*/*");
+        mActivity.startActivityForResult(intent, 42);
+
+        // Ensure that we see both of our roots
+        assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).exists());
+        assertTrue("CtsCreate root", new UiObject(new UiSelector().text("CtsCreate")).exists());
+        assertFalse("CtsGetContent", new UiObject(new UiSelector().text("CtsGetContent")).exists());
+
+        // Pick a specific file from our test provider
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().text("CtsLocal")).click();
+
+        // make sure drawer is expanded?
+
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().text("FILE1")).click();
+
+        final Result result = mActivity.getResult();
+        final Uri uri = result.data.getData();
+
+        // We should now have permission to read/write
+        MoreAsserts.assertEquals("fileone".getBytes(), readFully(uri));
+
+        writeFully(uri, "replaced!".getBytes());
+        SystemClock.sleep(500);
+        MoreAsserts.assertEquals("replaced!".getBytes(), readFully(uri));
+    }
+
+    public void testCreateNew() throws Exception {
+        if (!supportedHardware()) return;
+
+        final String DISPLAY_NAME = "My New Awesome Document Title";
+        final String MIME_TYPE = "image/png";
+
+        final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+        intent.addCategory(Intent.CATEGORY_OPENABLE);
+        intent.putExtra(Intent.EXTRA_TITLE, DISPLAY_NAME);
+        intent.setType(MIME_TYPE);
+        mActivity.startActivityForResult(intent, 42);
+
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().text("CtsCreate")).click();
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
+                .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
+
+        final Result result = mActivity.getResult();
+        final Uri uri = result.data.getData();
+
+        writeFully(uri, "meow!".getBytes());
+
+        assertEquals(DISPLAY_NAME, getColumn(uri, Document.COLUMN_DISPLAY_NAME));
+        assertEquals(MIME_TYPE, getColumn(uri, Document.COLUMN_MIME_TYPE));
+    }
+
+    public void testCreateExisting() throws Exception {
+        if (!supportedHardware()) return;
+
+        final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+        intent.addCategory(Intent.CATEGORY_OPENABLE);
+        intent.putExtra(Intent.EXTRA_TITLE, "NEVERUSED");
+        intent.setType("mime2/file2");
+        mActivity.startActivityForResult(intent, 42);
+
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().text("CtsCreate")).click();
+
+        // Pick file2, which should be selected since MIME matches, then try
+        // picking a non-matching MIME, which should leave file2 selected.
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().text("FILE2")).click();
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().text("FILE1")).click();
+
+        new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
+                .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
+
+        final Result result = mActivity.getResult();
+        final Uri uri = result.data.getData();
+
+        MoreAsserts.assertEquals("filetwo".getBytes(), readFully(uri));
+    }
+
+    public void testTree() throws Exception {
+        if (!supportedHardware()) return;
+
+        final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
+        mActivity.startActivityForResult(intent, 42);
+
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().text("CtsCreate")).click();
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().text("DIR2")).click();
+        mDevice.waitForIdle();
+        new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
+                .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
+
+        final Result result = mActivity.getResult();
+        final Uri uri = result.data.getData();
+
+        // We should have selected DIR2
+        Uri doc = DocumentsContract.buildDocumentUriUsingTree(uri,
+                DocumentsContract.getTreeDocumentId(uri));
+        Uri children = DocumentsContract.buildChildDocumentsUriUsingTree(uri,
+                DocumentsContract.getTreeDocumentId(uri));
+
+        assertEquals("DIR2", getColumn(doc, Document.COLUMN_DISPLAY_NAME));
+
+        // Look around and make sure we can see children
+        final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
+        Cursor cursor = resolver.query(children, new String[] {
+                Document.COLUMN_DISPLAY_NAME }, null, null, null);
+        try {
+            assertEquals(1, cursor.getCount());
+            assertTrue(cursor.moveToFirst());
+            assertEquals("FILE4", cursor.getString(0));
+        } finally {
+            cursor.close();
+        }
+
+        // Create some documents
+        Uri pic = DocumentsContract.createDocument(resolver, doc, "image/png", "pic.png");
+        Uri dir = DocumentsContract.createDocument(resolver, doc, Document.MIME_TYPE_DIR, "my dir");
+        Uri dirPic = DocumentsContract.createDocument(resolver, dir, "image/png", "pic2.png");
+
+        writeFully(pic, "pic".getBytes());
+        writeFully(dirPic, "dirPic".getBytes());
+
+        // Read then delete existing doc
+        final Uri file4 = DocumentsContract.buildDocumentUriUsingTree(uri, "doc:file4");
+        MoreAsserts.assertEquals("filefour".getBytes(), readFully(file4));
+        assertTrue("delete", DocumentsContract.deleteDocument(resolver, file4));
+        try {
+            MoreAsserts.assertEquals("filefour".getBytes(), readFully(file4));
+            fail("Expected file to be gone");
+        } catch (FileNotFoundException expected) {
+        }
+
+        // And rename something
+        dirPic = DocumentsContract.renameDocument(resolver, dirPic, "wow");
+        assertNotNull("rename", dirPic);
+
+        // We should only see single child
+        assertEquals("wow", getColumn(dirPic, Document.COLUMN_DISPLAY_NAME));
+        MoreAsserts.assertEquals("dirPic".getBytes(), readFully(dirPic));
+
+        try {
+            // Make sure we can't see files outside selected dir
+            getColumn(DocumentsContract.buildDocumentUriUsingTree(uri, "doc:file1"),
+                    Document.COLUMN_DISPLAY_NAME);
+            fail("Somehow read document outside tree!");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testGetContent() throws Exception {
+        if (!supportedHardware()) return;
+
+        final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+        intent.addCategory(Intent.CATEGORY_OPENABLE);
+        intent.setType("*/*");
+        mActivity.startActivityForResult(intent, 42);
+
+        mDevice.waitForIdle();
+
+        // Look around, we should be able to see both DocumentsProviders and
+        // other GET_CONTENT sources.
+        assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).exists());
+        assertTrue("CtsCreate root", new UiObject(new UiSelector().text("CtsCreate")).exists());
+        assertTrue("CtsGetContent", new UiObject(new UiSelector().text("CtsGetContent")).exists());
+
+        new UiObject(new UiSelector().text("CtsGetContent")).click();
+        mDevice.waitForIdle();
+
+        final Result result = mActivity.getResult();
+        assertEquals("ReSuLt", result.data.getAction());
+    }
+
+    private String getColumn(Uri uri, String column) {
+        final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
+        final Cursor cursor = resolver.query(uri, new String[] { column }, null, null, null);
+        try {
+            assertTrue(cursor.moveToFirst());
+            return cursor.getString(0);
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private byte[] readFully(Uri uri) throws IOException {
+        InputStream in = getInstrumentation().getContext().getContentResolver()
+                .openInputStream(uri);
+        try {
+            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+            byte[] buffer = new byte[1024];
+            int count;
+            while ((count = in.read(buffer)) != -1) {
+                bytes.write(buffer, 0, count);
+            }
+            return bytes.toByteArray();
+        } finally {
+            in.close();
+        }
+    }
+
+    private void writeFully(Uri uri, byte[] data) throws IOException {
+        OutputStream out = getInstrumentation().getContext().getContentResolver()
+                .openOutputStream(uri);
+        try {
+            out.write(data);
+        } finally {
+            out.close();
+        }
+    }
+
+    private boolean supportedHardware() {
+        final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        if (pm.hasSystemFeature("android.hardware.type.television")
+                || pm.hasSystemFeature("android.hardware.type.watch")) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/MyActivity.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/MyActivity.java
new file mode 100644
index 0000000..a6cb28d
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/MyActivity.java
@@ -0,0 +1,65 @@
+/*
+ * 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.documentclient;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+
+public class MyActivity extends Activity {
+    private final SynchronousQueue<Result> mResult = new SynchronousQueue<>();
+
+    public static class Result {
+        public final int resultCode;
+        public final Intent data;
+
+        public Result(int resultCode, Intent data) {
+            this.resultCode = resultCode;
+            this.data = data;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        try {
+            mResult.offer(new Result(resultCode, data), 5, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Result getResult() {
+        try {
+            return mResult.take();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.mk b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.mk
new file mode 100644
index 0000000..a886fb2
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.mk
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ctsdeviceutil ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsDocumentProvider
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml
new file mode 100644
index 0000000..c0fc6cc
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.cts.documentprovider">
+    <application>
+        <provider android:name=".MyDocumentsProvider"
+                android:authorities="com.android.cts.documentprovider"
+                android:exported="true"
+                android:grantUriPermissions="true"
+                android:permission="android.permission.MANAGE_DOCUMENTS">
+            <intent-filter>
+                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+            </intent-filter>
+        </provider>
+
+        <activity android:name=".GetContentActivity"
+                android:label="CtsGetContent">
+            <intent-filter>
+                <action android:name="android.intent.action.GET_CONTENT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.OPENABLE" />
+                <data android:mimeType="image/*" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/GetContentActivity.java b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/GetContentActivity.java
new file mode 100644
index 0000000..1aba526
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/GetContentActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.documentprovider;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class GetContentActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setResult(Activity.RESULT_OK, new Intent("ReSuLt"));
+        finish();
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
new file mode 100644
index 0000000..fb8993c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
@@ -0,0 +1,282 @@
+/*
+ * 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.documentprovider;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.os.AsyncTask;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsProvider;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MyDocumentsProvider extends DocumentsProvider {
+    private static final String TAG = "TestDocumentsProvider";
+
+    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
+            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
+            Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES,
+    };
+
+    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
+            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
+            Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
+    };
+
+    private static String[] resolveRootProjection(String[] projection) {
+        return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
+    }
+
+    private static String[] resolveDocumentProjection(String[] projection) {
+        return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
+    }
+
+    @Override
+    public boolean onCreate() {
+        resetRoots();
+        return true;
+    }
+
+    @Override
+    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
+
+        RowBuilder row = result.newRow();
+        row.add(Root.COLUMN_ROOT_ID, "local");
+        row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY);
+        row.add(Root.COLUMN_TITLE, "CtsLocal");
+        row.add(Root.COLUMN_SUMMARY, "CtsLocalSummary");
+        row.add(Root.COLUMN_DOCUMENT_ID, "doc:local");
+
+        row = result.newRow();
+        row.add(Root.COLUMN_ROOT_ID, "create");
+        row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD);
+        row.add(Root.COLUMN_TITLE, "CtsCreate");
+        row.add(Root.COLUMN_DOCUMENT_ID, "doc:create");
+
+        return result;
+    }
+
+    private Map<String, Doc> mDocs = new HashMap<>();
+
+    private Doc mLocalRoot;
+    private Doc mCreateRoot;
+
+    private Doc buildDoc(String docId, String displayName, String mimeType) {
+        final Doc doc = new Doc();
+        doc.docId = docId;
+        doc.displayName = displayName;
+        doc.mimeType = mimeType;
+        mDocs.put(doc.docId, doc);
+        return doc;
+    }
+
+    public void resetRoots() {
+        Log.d(TAG, "resetRoots()");
+
+        mDocs.clear();
+
+        mLocalRoot = buildDoc("doc:local", null, Document.MIME_TYPE_DIR);
+
+        mCreateRoot = buildDoc("doc:create", null, Document.MIME_TYPE_DIR);
+        mCreateRoot.flags = Document.FLAG_DIR_SUPPORTS_CREATE;
+
+        {
+            Doc file1 = buildDoc("doc:file1", "FILE1", "mime1/file1");
+            file1.contents = "fileone".getBytes();
+            file1.flags = Document.FLAG_SUPPORTS_WRITE;
+            mLocalRoot.children.add(file1);
+            mCreateRoot.children.add(file1);
+        }
+
+        {
+            Doc file2 = buildDoc("doc:file2", "FILE2", "mime2/file2");
+            file2.contents = "filetwo".getBytes();
+            file2.flags = Document.FLAG_SUPPORTS_WRITE;
+            mLocalRoot.children.add(file2);
+            mCreateRoot.children.add(file2);
+        }
+
+        Doc dir1 = buildDoc("doc:dir1", "DIR1", Document.MIME_TYPE_DIR);
+        mLocalRoot.children.add(dir1);
+
+        {
+            Doc file3 = buildDoc("doc:file3", "FILE3", "mime3/file3");
+            file3.contents = "filethree".getBytes();
+            file3.flags = Document.FLAG_SUPPORTS_WRITE;
+            dir1.children.add(file3);
+        }
+
+        Doc dir2 = buildDoc("doc:dir2", "DIR2", Document.MIME_TYPE_DIR);
+        mCreateRoot.children.add(dir2);
+
+        {
+            Doc file4 = buildDoc("doc:file4", "FILE4", "mime4/file4");
+            file4.contents = "filefour".getBytes();
+            file4.flags = Document.FLAG_SUPPORTS_WRITE;
+            dir2.children.add(file4);
+        }
+    }
+
+    private static class Doc {
+        public String docId;
+        public int flags;
+        public String displayName;
+        public long size;
+        public String mimeType;
+        public long lastModified;
+        public byte[] contents;
+        public List<Doc> children = new ArrayList<>();
+
+        public void include(MatrixCursor result) {
+            final RowBuilder row = result.newRow();
+            row.add(Document.COLUMN_DOCUMENT_ID, docId);
+            row.add(Document.COLUMN_DISPLAY_NAME, displayName);
+            row.add(Document.COLUMN_SIZE, size);
+            row.add(Document.COLUMN_MIME_TYPE, mimeType);
+            row.add(Document.COLUMN_FLAGS, flags);
+            row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
+        }
+    }
+
+    @Override
+    public boolean isChildDocument(String parentDocumentId, String documentId) {
+        for (Doc doc : mDocs.get(parentDocumentId).children) {
+            if (doc.docId.equals(documentId)) {
+                return true;
+            }
+            if (Document.MIME_TYPE_DIR.equals(doc.mimeType)) {
+                return isChildDocument(doc.docId, documentId);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String createDocument(String parentDocumentId, String mimeType, String displayName)
+            throws FileNotFoundException {
+        final String docId = "doc:" + System.currentTimeMillis();
+        final Doc doc = buildDoc(docId, displayName, mimeType);
+        doc.flags = Document.FLAG_SUPPORTS_WRITE | Document.FLAG_SUPPORTS_RENAME;
+        mDocs.get(parentDocumentId).children.add(doc);
+        return docId;
+    }
+
+    @Override
+    public String renameDocument(String documentId, String displayName)
+            throws FileNotFoundException {
+        mDocs.get(documentId).displayName = displayName;
+        return null;
+    }
+
+    @Override
+    public void deleteDocument(String documentId) throws FileNotFoundException {
+        mDocs.remove(documentId);
+        for (Doc doc : mDocs.values()) {
+            doc.children.remove(documentId);
+        }
+    }
+
+    @Override
+    public Cursor queryDocument(String documentId, String[] projection)
+            throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+        mDocs.get(documentId).include(result);
+        return result;
+    }
+
+    @Override
+    public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
+            String sortOrder) throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+        for (Doc doc : mDocs.get(parentDocumentId).children) {
+            doc.include(result);
+        }
+        return result;
+    }
+
+    @Override
+    public ParcelFileDescriptor openDocument(String documentId, String mode,
+            CancellationSignal signal) throws FileNotFoundException {
+        final Doc doc = mDocs.get(documentId);
+        if (doc == null) {
+            throw new FileNotFoundException();
+        }
+        final ParcelFileDescriptor[] pipe;
+        try {
+            pipe = ParcelFileDescriptor.createPipe();
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        }
+        if (mode.contains("w")) {
+            new AsyncTask<Void, Void, Void>() {
+                @Override
+                protected Void doInBackground(Void... params) {
+                    try {
+                        final InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
+                                pipe[0]);
+                        doc.contents = readFullyNoClose(is);
+                        is.close();
+                    } catch (IOException e) {
+                        Log.w(TAG, "Failed to stream", e);
+                    }
+                    return null;
+                }
+            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+            return pipe[1];
+        } else {
+            new AsyncTask<Void, Void, Void>() {
+                @Override
+                protected Void doInBackground(Void... params) {
+                    try {
+                        final OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(
+                                pipe[1]);
+                        os.write(doc.contents);
+                        os.close();
+                    } catch (IOException e) {
+                        Log.w(TAG, "Failed to stream", e);
+                    }
+                    return null;
+                }
+            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+            return pipe[0];
+        }
+    }
+
+    private static byte[] readFullyNoClose(InputStream in) throws IOException {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int count;
+        while ((count = in.read(buffer)) != -1) {
+            bytes.write(buffer, 0, count);
+        }
+        return bytes.toByteArray();
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/keysets/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/Android.mk
new file mode 100644
index 0000000..935fa95
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/Android.mk
@@ -0,0 +1,21 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk
new file mode 100644
index 0000000..eb71540
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk
@@ -0,0 +1,41 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#apks signed cts-keyset-test-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetPermDefSigningA
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+#apks signed cts-keyset-test-b
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetPermDefSigningB
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permDef/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/permDef/AndroidManifest.xml
new file mode 100644
index 0000000..7b84f6a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/permDef/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.keysets_permdef">
+    <application android:hasCode="false">
+    </application>
+    <permission android:description="@string/keysets_perm_desc"
+                android:label="@string/keysets_perm_label"
+                android:name="com.android.cts.keysets_permdef.keysets_perm"
+                android:protectionLevel="signature" />
+    <key-sets>
+        <key-set android:name="A" >
+          <public-key android:name="keyA"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwf5zJblvYSB7Ym7or/7GggAAu7mp7RrykPJsXhod8doFhVT5s7eF3A4MCE55vvANP7HvwMw2b+T6qx7Pq0VJtbbSDtlBHBtIc47Pjq0CsDg590BUcgKp7PdJ9J6UVgtzDnV6cGEpXmSag3sY+lqiW04ytPhCVwzYTWGdYe9+TIl47cBrveRfLOlGrcuFQe+zCTmDFqzBKCRHK9b7l5PDWvXXyg65Uu/MBUA/TZWO0fEqOlxZG/nn6DUKQLhPdmJRXWJ3WqMNMhJGD+nKtkmdX703xRqmg4h+6g0S7M9Y3IQ2NUGyw05AYzCguHB/Mv6uVIiW659wpbyb45TgKG3UhQIDAQAB" />
+        </key-set>
+        <key-set android:name="B" >
+          <public-key android:name="keyB"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoeFZqMqTbZiozFTXMkXtSKJRzn2qODZgvVXAAwKTi50xYcbPcHTfKxtif8+q7OCp/50JYDH32bg6wkUunn5+dEaHkxZY8d7uw46tQtl5dNGi+6cc4MezVLCS6nkqNDusAgdvgLU6Fl6SGi02KTp1vkt6CwLO977YJP7kt9ouDRTG7ASJiq3OyRRoOqYHhD9gpsbUq4w+1bXGfuuZujA1dXyovXtvrHUGOdFIEBYOVYGfCcwh3lXPmjNJMlHtKQkurq8/LH7a1B5ocoXCGsyR8YHdlWfrqRAfzgOB1KCnNNmWqskU9LOci3uQn9IDeMEFmAd8FqF8SwV+4Ludk/xWGQIDAQAB" />
+        </key-set>
+        <upgrade-key-set android:name="A"/>
+        <upgrade-key-set android:name="B"/>
+    </key-sets>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permDef/res/values/strings.xml b/hostsidetests/appsecurity/test-apps/keysets/permDef/res/values/strings.xml
new file mode 100644
index 0000000..4e5e870
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/permDef/res/values/strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="keysets_perm_desc">keysets_perm_description</string>
+  <string name="keysets_perm_label">keysets_perm_label</string>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk
new file mode 100644
index 0000000..000b12a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk
@@ -0,0 +1,41 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#apks signed cts-keyset-test-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetPermUseSigningA
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+#apks signed cts-keyset-test-b
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetPermUseSigningB
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permUse/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/permUse/AndroidManifest.xml
new file mode 100644
index 0000000..40ca1cb
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/permUse/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.keysets">
+    <application android:hasCode="false">
+    </application>
+    <uses-permission android:name="com.android.cts.keysets_permdef.keysets_perm" />
+    <key-sets>
+        <key-set android:name="A">
+          <public-key android:name="keyA"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwf5zJblvYSB7Ym7or/7GggAAu7mp7RrykPJsXhod8doFhVT5s7eF3A4MCE55vvANP7HvwMw2b+T6qx7Pq0VJtbbSDtlBHBtIc47Pjq0CsDg590BUcgKp7PdJ9J6UVgtzDnV6cGEpXmSag3sY+lqiW04ytPhCVwzYTWGdYe9+TIl47cBrveRfLOlGrcuFQe+zCTmDFqzBKCRHK9b7l5PDWvXXyg65Uu/MBUA/TZWO0fEqOlxZG/nn6DUKQLhPdmJRXWJ3WqMNMhJGD+nKtkmdX703xRqmg4h+6g0S7M9Y3IQ2NUGyw05AYzCguHB/Mv6uVIiW659wpbyb45TgKG3UhQIDAQAB" />
+        </key-set>
+        <key-set android:name="B">
+          <public-key android:name="keyB"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoeFZqMqTbZiozFTXMkXtSKJRzn2qODZgvVXAAwKTi50xYcbPcHTfKxtif8+q7OCp/50JYDH32bg6wkUunn5+dEaHkxZY8d7uw46tQtl5dNGi+6cc4MezVLCS6nkqNDusAgdvgLU6Fl6SGi02KTp1vkt6CwLO977YJP7kt9ouDRTG7ASJiq3OyRRoOqYHhD9gpsbUq4w+1bXGfuuZujA1dXyovXtvrHUGOdFIEBYOVYGfCcwh3lXPmjNJMlHtKQkurq8/LH7a1B5ocoXCGsyR8YHdlWfrqRAfzgOB1KCnNNmWqskU9LOci3uQn9IDeMEFmAd8FqF8SwV+4Ludk/xWGQIDAQAB" />
+        </key-set>
+        <upgrade-key-set android:name="A"/>
+        <upgrade-key-set android:name="B"/>
+    </key-sets>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permUse/res/values/strings.xml b/hostsidetests/appsecurity/test-apps/keysets/permUse/res/values/strings.xml
new file mode 100644
index 0000000..4e5e870
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/permUse/res/values/strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="keysets_perm_desc">keysets_perm_description</string>
+  <string name="keysets_perm_label">keysets_perm_label</string>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/keysets/testApp/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/testApp/Android.mk
new file mode 100644
index 0000000..ed6db69
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/testApp/Android.mk
@@ -0,0 +1,26 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetTestApp
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/testApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/testApp/AndroidManifest.xml
new file mode 100644
index 0000000..38edf5f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/testApp/AndroidManifest.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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.keysets.testapp">
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:targetPackage="com.android.cts.keysets.testapp"
+        android:name="android.support.test.runner.AndroidJUnitRunner" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/keysets/testApp/src/com/android/cts/keysets/KeySetPermissionsTest.java b/hostsidetests/appsecurity/test-apps/keysets/testApp/src/com/android/cts/keysets/KeySetPermissionsTest.java
new file mode 100644
index 0000000..467a212
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/testApp/src/com/android/cts/keysets/KeySetPermissionsTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.keysets;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import android.test.AndroidTestCase;
+
+import java.lang.Override;
+
+/**
+ * KeySets device-side tests involving permissions
+ */
+public class KeySetPermissionsTest extends AndroidTestCase {
+
+    private static final String KEYSET_APP_PKG = "com.android.cts.keysets";
+    private static final String KEYSET_PERM_DEF_PKG = "com.android.cts.keysets_permdef";
+    private static final String KEYSET_PERM_NAME = "com.android.cts.keysets_permdef.keysets_perm";
+
+    public void testHasPerm() throws Exception {
+        PackageManager pm = getContext().getPackageManager();
+        assertTrue(KEYSET_PERM_NAME + " not granted to " + KEYSET_APP_PKG,
+                pm.checkPermission(KEYSET_PERM_NAME, KEYSET_APP_PKG) == PackageManager.PERMISSION_GRANTED);
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
new file mode 100644
index 0000000..6220790
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
@@ -0,0 +1,53 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#apks signed by cts-keyset-test-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeA
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+#apks signed by cts-keyset-test-b
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningBUpgradeA
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+#apks signed by cts-keyset-test-a and cts-keyset-test-b
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningAAndBUpgradeA
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_ADDITIONAL_CERTIFICATES := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b
+LOCAL_DEX_PREOPT := false
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uA/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/uA/AndroidManifest.xml
new file mode 100644
index 0000000..8813c37
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uA/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.keysets">
+    <application android:hasCode="false">
+    </application>
+    <key-sets>
+        <key-set android:name="A" >
+          <public-key android:name="keyA"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwf5zJblvYSB7Ym7or/7GggAAu7mp7RrykPJsXhod8doFhVT5s7eF3A4MCE55vvANP7HvwMw2b+T6qx7Pq0VJtbbSDtlBHBtIc47Pjq0CsDg590BUcgKp7PdJ9J6UVgtzDnV6cGEpXmSag3sY+lqiW04ytPhCVwzYTWGdYe9+TIl47cBrveRfLOlGrcuFQe+zCTmDFqzBKCRHK9b7l5PDWvXXyg65Uu/MBUA/TZWO0fEqOlxZG/nn6DUKQLhPdmJRXWJ3WqMNMhJGD+nKtkmdX703xRqmg4h+6g0S7M9Y3IQ2NUGyw05AYzCguHB/Mv6uVIiW659wpbyb45TgKG3UhQIDAQAB" />
+        </key-set>
+        <upgrade-key-set android:name="A"/>
+    </key-sets>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk
new file mode 100644
index 0000000..534dba3
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk
@@ -0,0 +1,28 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#apks signed cts-keyset-test-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeAAndB
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uAB/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/uAB/AndroidManifest.xml
new file mode 100644
index 0000000..65f78e3
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uAB/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.keysets">
+    <application android:hasCode="false">
+    </application>
+    <key-sets>
+        <key-set android:name="AB" >
+          <public-key android:name="keyA"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwf5zJblvYSB7Ym7or/7GggAAu7mp7RrykPJsXhod8doFhVT5s7eF3A4MCE55vvANP7HvwMw2b+T6qx7Pq0VJtbbSDtlBHBtIc47Pjq0CsDg590BUcgKp7PdJ9J6UVgtzDnV6cGEpXmSag3sY+lqiW04ytPhCVwzYTWGdYe9+TIl47cBrveRfLOlGrcuFQe+zCTmDFqzBKCRHK9b7l5PDWvXXyg65Uu/MBUA/TZWO0fEqOlxZG/nn6DUKQLhPdmJRXWJ3WqMNMhJGD+nKtkmdX703xRqmg4h+6g0S7M9Y3IQ2NUGyw05AYzCguHB/Mv6uVIiW659wpbyb45TgKG3UhQIDAQAB" />
+          <public-key android:name="keyB"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoeFZqMqTbZiozFTXMkXtSKJRzn2qODZgvVXAAwKTi50xYcbPcHTfKxtif8+q7OCp/50JYDH32bg6wkUunn5+dEaHkxZY8d7uw46tQtl5dNGi+6cc4MezVLCS6nkqNDusAgdvgLU6Fl6SGi02KTp1vkt6CwLO977YJP7kt9ouDRTG7ASJiq3OyRRoOqYHhD9gpsbUq4w+1bXGfuuZujA1dXyovXtvrHUGOdFIEBYOVYGfCcwh3lXPmjNJMlHtKQkurq8/LH7a1B5ocoXCGsyR8YHdlWfrqRAfzgOB1KCnNNmWqskU9LOci3uQn9IDeMEFmAd8FqF8SwV+4Ludk/xWGQIDAQAB" />
+        </key-set>
+        <upgrade-key-set android:name="AB"/>
+    </key-sets>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk
new file mode 100644
index 0000000..75729e0
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk
@@ -0,0 +1,28 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#apks signed cts-keyset-test-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeAOrB
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uAuB/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/uAuB/AndroidManifest.xml
new file mode 100644
index 0000000..546c5fe
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uAuB/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.keysets">
+    <application android:hasCode="false">
+    </application>
+    <key-sets>
+        <key-set android:name="A" >
+          <public-key android:name="keyA"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwf5zJblvYSB7Ym7or/7GggAAu7mp7RrykPJsXhod8doFhVT5s7eF3A4MCE55vvANP7HvwMw2b+T6qx7Pq0VJtbbSDtlBHBtIc47Pjq0CsDg590BUcgKp7PdJ9J6UVgtzDnV6cGEpXmSag3sY+lqiW04ytPhCVwzYTWGdYe9+TIl47cBrveRfLOlGrcuFQe+zCTmDFqzBKCRHK9b7l5PDWvXXyg65Uu/MBUA/TZWO0fEqOlxZG/nn6DUKQLhPdmJRXWJ3WqMNMhJGD+nKtkmdX703xRqmg4h+6g0S7M9Y3IQ2NUGyw05AYzCguHB/Mv6uVIiW659wpbyb45TgKG3UhQIDAQAB" />
+        </key-set>
+        <key-set android:name="B" >
+          <public-key android:name="keyB"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoeFZqMqTbZiozFTXMkXtSKJRzn2qODZgvVXAAwKTi50xYcbPcHTfKxtif8+q7OCp/50JYDH32bg6wkUunn5+dEaHkxZY8d7uw46tQtl5dNGi+6cc4MezVLCS6nkqNDusAgdvgLU6Fl6SGi02KTp1vkt6CwLO977YJP7kt9ouDRTG7ASJiq3OyRRoOqYHhD9gpsbUq4w+1bXGfuuZujA1dXyovXtvrHUGOdFIEBYOVYGfCcwh3lXPmjNJMlHtKQkurq8/LH7a1B5ocoXCGsyR8YHdlWfrqRAfzgOB1KCnNNmWqskU9LOci3uQn9IDeMEFmAd8FqF8SwV+4Ludk/xWGQIDAQAB" />
+        </key-set>
+        <upgrade-key-set android:name="A"/>
+        <upgrade-key-set android:name="B"/>
+    </key-sets>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk
new file mode 100644
index 0000000..121c342
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk
@@ -0,0 +1,55 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#apks signed cts-keyset-test-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeB
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+#apks signed cts-keyset-test-b
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningBUpgradeB
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-b
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+#apks signed by cts-keyset-test-a and cts-keyset-test-c
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningAAndCUpgradeB
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_ADDITIONAL_CERTIFICATES := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-c
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uB/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/uB/AndroidManifest.xml
new file mode 100644
index 0000000..9c837bc
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uB/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.keysets">
+    <application android:hasCode="false">
+    </application>
+    <key-sets>
+        <key-set android:name="B" >
+          <public-key android:name="keyB"
+                      android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoeFZqMqTbZiozFTXMkXtSKJRzn2qODZgvVXAAwKTi50xYcbPcHTfKxtif8+q7OCp/50JYDH32bg6wkUunn5+dEaHkxZY8d7uw46tQtl5dNGi+6cc4MezVLCS6nkqNDusAgdvgLU6Fl6SGi02KTp1vkt6CwLO977YJP7kt9ouDRTG7ASJiq3OyRRoOqYHhD9gpsbUq4w+1bXGfuuZujA1dXyovXtvrHUGOdFIEBYOVYGfCcwh3lXPmjNJMlHtKQkurq8/LH7a1B5ocoXCGsyR8YHdlWfrqRAfzgOB1KCnNNmWqskU9LOci3uQn9IDeMEFmAd8FqF8SwV+4Ludk/xWGQIDAQAB" />
+        </key-set>
+        <upgrade-key-set android:name="B"/>
+    </key-sets>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk
new file mode 100644
index 0000000..a8746ec
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk
@@ -0,0 +1,28 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#apks signed cts-keyset-test-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeNone
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uNone/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/uNone/AndroidManifest.xml
new file mode 100644
index 0000000..55304f4
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uNone/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.keysets">
+    <application android:hasCode="false">
+    </application>
+</manifest>
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 40a66d2..8adf345 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -143,7 +143,7 @@
 {
   description: "This test failed on devices that use effect off loading. In addition it uses hidden apis",
   names: [
-    "android.meida.cts.AudioEffectTest#test1_1ConstructorFromUuid"
+    "android.media.cts.AudioEffectTest#test1_1ConstructorFromUuid"
   ],
   bug: 17605875
 },
@@ -319,7 +319,7 @@
 {
   description: "This test failed on hw decoder that doesn't output frame with the configured format.",
   names: [
-    "android.meida.cts.ImageReaderDecoderTest#testHwAVCDecode360pForFlexibleYuv"
+    "android.media.cts.ImageReaderDecoderTest#testHwAVCDecode360pForFlexibleYuv"
   ],
   bug: 17144778
 }
diff --git a/tests/tests/graphics/res/drawable/vector_icon_delete.xml b/tests/tests/graphics/res/drawable/vector_icon_delete.xml
index 8d9c21c..7b8f2aa 100644
--- a/tests/tests/graphics/res/drawable/vector_icon_delete.xml
+++ b/tests/tests/graphics/res/drawable/vector_icon_delete.xml
@@ -24,6 +24,6 @@
 
     <path
         android:fillColor="#FF000000"
-        android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0-0.896 2.0-2.0l0.0-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
+        android:pathData="M6.0,19.0c0.0,1.104 896e-3,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0-896e-3 2.0-2.0l0.0-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/drawable/vector_icon_heart.xml b/tests/tests/graphics/res/drawable/vector_icon_heart.xml
index ff55fe5..ad991c9 100644
--- a/tests/tests/graphics/res/drawable/vector_icon_heart.xml
+++ b/tests/tests/graphics/res/drawable/vector_icon_heart.xml
@@ -24,6 +24,6 @@
 
     <path
         android:fillColor="#FF000000"
-        android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67-1.732 -2.547-3.0 -4.5-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207-5.242 9.0-7.971 9.0-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
+        android:pathData="M16.0,5.0c-1.955.0 -3.83,1.268 -4.5,3.0c-0.67-1.732 -2.547-3.0 -4.5-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207-5.242 9.0-7.971 9.0-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index a4d4157..d8b8e51 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -45,8 +45,8 @@
     private static final String TAG = "SensorTest";
     // Test only SDK defined sensors. Any sensors with type > 100 are ignored.
     private static final int MAX_OFFICIAL_ANDROID_SENSOR_TYPE = 100;
-    private static final long TIMEOUT_TOLERANCE_US = TimeUnit.SECONDS.toMicros(10);
-
+    private static final long TIMEOUT_TOLERANCE_US = TimeUnit.SECONDS.toMicros(5);
+    private static final double MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE = 0.9;
     private PowerManager.WakeLock mWakeLock;
 
     @Override
@@ -272,13 +272,17 @@
 
             final long elapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
 
-            if (elapsedRealtimeNs - currTimeStampNs > SYNC_TOLERANCE + mEventReportLatencyNs) {
+            if (elapsedRealtimeNs <= currTimeStampNs) {
+                Log.w(TAG, "Timestamps into the future curr elapsedRealTimeNs=" + elapsedRealtimeNs
+                         + " current sensor ts_ns=" + currTimeStampNs);
+                ++numErrors;
+            } else if (elapsedRealtimeNs-currTimeStampNs > SYNC_TOLERANCE + mEventReportLatencyNs) {
                 Log.w(TAG, "Timestamp sync error elapsedRealTimeNs=" + elapsedRealtimeNs +
                         " curr_ts_ns=" + currTimeStampNs +
                         " diff_ns=" + (elapsedRealtimeNs - currTimeStampNs) +
                         " SYNC_TOLERANCE_NS=" + SYNC_TOLERANCE +
                         " eventReportLatencyNs=" + mEventReportLatencyNs);
-                numErrors++;
+                ++numErrors;
             }
             mPrevTimeStampNs = currTimeStampNs;
         }
@@ -302,8 +306,10 @@
     // elapsedRealTimeNano.
     @TimeoutReq(minutes=60)
     public void testSensorTimeStamps() throws Exception {
+        final int numEvents = 2000;
         try {
             mWakeLock.acquire();
+            int numErrors = 0;
             for (Sensor sensor : mSensorList) {
                 // Skip OEM defined sensors and non continuous sensors.
                 if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
@@ -315,35 +321,45 @@
                     long maxBatchReportLatencyNs = 10000000000L; // 10 secs
                     if (iterations % 2 == 0) maxBatchReportLatencyNs = 0;
 
-                    final long samplingPeriodNs = 20000000;
-                    // If there is a FIFO and a wake-lock is held, events will be reported when the
-                    // batch timeout expires or when the FIFO is full which ever occurs earlier.
+                    final long samplingPeriodNs = (long)(TimeUnit.MICROSECONDS.toNanos(
+                            sensor.getMinDelay())/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE);
+                    // If there is a FIFO and a wake-lock is held, events will be reported when
+                    // the batch timeout expires or when the FIFO is full which ever occurs
+                    // earlier.
                     final long eventReportLatencyNs = Math.min(maxBatchReportLatencyNs,
                             sensor.getFifoMaxEventCount() * samplingPeriodNs);
 
-                    final CountDownLatch eventReceivedLatch = new CountDownLatch(2000);
+                    final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
                     SensorEventTimeStampListener listener = new SensorEventTimeStampListener(
-                            eventReportLatencyNs, eventReceivedLatch);
+                            eventReportLatencyNs, eventsRemaining);
 
                     Log.i(TAG, "Running timeStamp test on " + sensor.getName());
                     boolean result = mSensorManager.registerListener(listener, sensor,
-                            SensorManager.SENSOR_DELAY_GAME, (int)maxBatchReportLatencyNs/1000);
+                            SensorManager.SENSOR_DELAY_FASTEST,
+                            (int)maxBatchReportLatencyNs/1000);
                     assertTrue("Sensor registerListener failed ", result);
 
-                    // Wait for 300 seconds.
-                    boolean countZero = eventReceivedLatch.await(300, TimeUnit.SECONDS);
-                    if (!countZero) {
-                        fail("Timed out waiting for events from " + sensor.getName());
+                    long timeToWaitUs = samplingPeriodNs/1000 + eventReportLatencyNs/1000 +
+                            TIMEOUT_TOLERANCE_US;
+                    long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
+                            (int)(eventReportLatencyNs/1000), eventsRemaining);
+
+                    mSensorManager.unregisterListener(listener);
+                    if (eventsRemaining.getCount() > 0) {
+                        failTimedOut(sensor.getName(), (double) totalTimeWaitedUs/1000,
+                                numEvents, (double) sensor.getMinDelay()/1000,
+                                eventsRemaining.getCount(),
+                                numEvents - eventsRemaining.getCount());
                     }
                     if (listener.getNumErrors() > 5) {
                         fail("Check logcat. Timestamp test failed. numErrors=" +
                                 listener.getNumErrors() + " " + sensor.getName() +
                                 " maxBatchReportLatencyNs=" + maxBatchReportLatencyNs +
-                                " samplingPeriodNs=" + samplingPeriodNs);
+                                " samplingPeriodNs=" + sensor.getMinDelay());
+                        numErrors += listener.getNumErrors();
                     } else {
                         Log.i(TAG, "TimeStamp test PASS'd on " + sensor.getName());
                     }
-                    mSensorManager.unregisterListener(listener);
                 }
             }
         } finally {
@@ -351,9 +367,18 @@
         }
     }
 
+    private void failTimedOut(String sensorName, double totalTimeWaitedMs, int numEvents,
+            double minDelayMs, long eventsRemaining, long eventsReceived) {
+        final String TIMED_OUT_FORMAT = "Timed out waiting for events from %s " +
+                "waited for time=%.1fms to receive totalEvents=%d at samplingRate=%.1fHz" +
+                " remainingEvents=%d  received events=%d";
+        fail(String.format(TIMED_OUT_FORMAT, sensorName, totalTimeWaitedMs, numEvents,
+                1000/minDelayMs, eventsRemaining, eventsReceived));
+    }
+
     // Register for updates from each continuous mode sensor, wait for N events, call flush and
     // wait for flushCompleteEvent before unregistering for the sensor.
-    @TimeoutReq(minutes=10)
+    @TimeoutReq(minutes=20)
     public void testBatchAndFlush() throws Exception {
         try {
             mWakeLock.acquire();
@@ -394,14 +419,14 @@
             return;
         }
         final int numEvents = 500;
-        final int rateUs = 20000; // DELAY_GAME
+        final int rateUs = 0; // DELAY_FASTEST
         final int maxBatchReportLatencyUs = 10000000;
-        final CountDownLatch eventReceived = new CountDownLatch(numEvents);
+        final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
         final CountDownLatch flushReceived = new CountDownLatch(1);
         SensorEventListener2 listener = new SensorEventListener2() {
             @Override
             public void onSensorChanged(SensorEvent event) {
-                eventReceived.countDown();
+                eventsRemaining.countDown();
             }
 
             @Override
@@ -416,30 +441,62 @@
         // Consider only continuous mode sensors for testing registerListener.
         // For on-change sensors, call registerListener() so that the listener is associated
         // with the sensor so that flush(listener) can be called on it.
-        Log.i(TAG, "testBatch " + sensor.getName());
-        boolean result = mSensorManager.registerListener(listener, sensor,
-                rateUs, maxBatchReportLatencyUs, handler);
-        assertTrue("registerListener failed " + sensor.getName(), result);
-        // Wait for 500 events or N seconds before the test times out.
-        if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
-            // Wait for approximately the time required to generate these events + a tolerance
-            // of 10 seconds.
-            long timeToWaitUs = (long)numEvents * rateUs + maxBatchReportLatencyUs +
-                    TIMEOUT_TOLERANCE_US;
-            boolean countZero = eventReceived.await(timeToWaitUs, TimeUnit.MICROSECONDS);
-            if (!countZero) {
-                fail("Timed out waiting for events from " + sensor.getName());
+        try {
+            Log.i(TAG, "testBatch " + sensor.getName());
+            boolean result = mSensorManager.registerListener(listener, sensor,
+                    rateUs, maxBatchReportLatencyUs, handler);
+            assertTrue("registerListener failed " + sensor.getName(), result);
+
+            // Wait for 500 events or N seconds before the test times out.
+            if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
+                // Wait for approximately the time required to generate these events + a tolerance
+                // of 10 seconds.
+                long timeToWaitUs =
+                  numEvents*(long)(sensor.getMinDelay()/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE)
+                        + maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
+
+                long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
+                        maxBatchReportLatencyUs, eventsRemaining);
+                if (eventsRemaining.getCount() > 0) {
+                    failTimedOut(sensor.getName(), (double)totalTimeWaitedUs/1000, numEvents,
+                            (double)sensor.getMinDelay()/1000,
+                            eventsRemaining.getCount(), numEvents - eventsRemaining.getCount());
+                }
             }
+            Log.i(TAG, "testFlush " + sensor.getName());
+            result = mSensorManager.flush(listener);
+            assertTrue("flush failed " + sensor.getName(), result);
+            boolean collectedAllEvents = flushReceived.await(TIMEOUT_TOLERANCE_US,
+                    TimeUnit.MICROSECONDS);
+            if (!collectedAllEvents) {
+                fail("Timed out waiting for flushCompleteEvent from " + sensor.getName() +
+                        " waitedFor="+ TIMEOUT_TOLERANCE_US/1000 + "ms");
+            }
+            Log.i(TAG, "testBatchAndFlush PASS " + sensor.getName());
+        } finally {
+            mSensorManager.unregisterListener(listener);
         }
-        Log.i(TAG, "testFlush " + sensor.getName());
-        result = mSensorManager.flush(listener);
-        assertTrue("flush failed " + sensor.getName(), result);
-        boolean countZero = flushReceived.await(TIMEOUT_TOLERANCE_US, TimeUnit.MICROSECONDS);
-        if (!countZero) {
-            fail("Timed out waiting for flushCompleteEvent from " + sensor.getName());
-        }
-        mSensorManager.unregisterListener(listener);
-        Log.i(TAG, "testBatchAndFlush pass " + sensor.getName());
+    }
+
+    // Wait till the CountDownLatch counts down to zero. If the events are not delivered within
+    // timetoWaitUs, wait an additional maxReportLantencyUs and check if the sensor is streaming
+    // data or not. If the sensor is not streaming at all, fail the test or else wait in increments
+    // of maxReportLantencyUs to collect sensor events.
+    private long waitToCollectAllEvents(long timeToWaitUs, int maxReportLatencyUs,
+            CountDownLatch eventsRemaining)
+            throws InterruptedException {
+        boolean collectedAllEvents = false;
+        long totalTimeWaitedUs = 0;
+        long remainingEvents;
+        final long INCREMENTAL_WAIT_US = maxReportLatencyUs + TimeUnit.SECONDS.toMicros(1);
+        do {
+            totalTimeWaitedUs += timeToWaitUs;
+            remainingEvents = eventsRemaining.getCount();
+            collectedAllEvents = eventsRemaining.await(timeToWaitUs, TimeUnit.MICROSECONDS);
+            timeToWaitUs = INCREMENTAL_WAIT_US;
+        } while (!collectedAllEvents &&
+                (remainingEvents - eventsRemaining.getCount() >=(long)INCREMENTAL_WAIT_US/1000000));
+        return totalTimeWaitedUs;
     }
 
     // Call registerListener for multiple sensors at a time and call flush.
@@ -450,14 +507,14 @@
             return;
         }
         final int numEvents = 500;
-        final int rateUs = 20000;
+        int rateUs = 0; // DELAY_FASTEST
         final int maxBatchReportLatencyUs = 10000000;
-        final CountDownLatch eventReceived = new CountDownLatch(numSensors * numEvents);
+        final CountDownLatch eventsRemaining = new CountDownLatch(numSensors * numEvents);
         final CountDownLatch flushReceived = new CountDownLatch(numSensors);
         SensorEventListener2 listener = new SensorEventListener2() {
             @Override
             public void onSensorChanged(SensorEvent event) {
-                eventReceived.countDown();
+                eventsRemaining.countDown();
             }
 
             @Override
@@ -478,8 +535,9 @@
                 if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
                     continue;
                 }
+                rateUs = Math.max(sensor.getMinDelay(), rateUs);
                 boolean result = mSensorManager.registerListener(listener, sensor,
-                        SensorManager.SENSOR_DELAY_GAME, 10000000);
+                        SensorManager.SENSOR_DELAY_FASTEST, maxBatchReportLatencyUs);
                 assertTrue("registerListener failed for " + sensor.getName(), result);
                 registeredSensors.append(sensor.getName());
                 registeredSensors.append(" ");
@@ -492,41 +550,50 @@
             }
 
             Log.i(TAG, "testBatchAndFlushWithMutipleSensors " + registeredSensors);
-            long timeToWaitUs = (long)numEvents * rateUs + maxBatchReportLatencyUs +
-                    TIMEOUT_TOLERANCE_US;
-            boolean countZero = eventReceived.await(timeToWaitUs, TimeUnit.MICROSECONDS);
-            if (!countZero) {
-                fail("Timed out waiting for events from " + registeredSensors.toString());
+            long timeToWaitUs =
+                    numEvents*(long)(rateUs/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE) +
+                    maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
+            long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs, maxBatchReportLatencyUs,
+                    eventsRemaining);
+            if (eventsRemaining.getCount() > 0) {
+                failTimedOut(registeredSensors.toString(), (double)totalTimeWaitedUs/1000,
+                        numEvents, (double)rateUs/1000, eventsRemaining.getCount(),
+                        numEvents - eventsRemaining.getCount());
             }
             boolean result = mSensorManager.flush(listener);
             assertTrue("flush failed " + registeredSensors.toString(), result);
-            countZero = flushReceived.await(TIMEOUT_TOLERANCE_US, TimeUnit.MICROSECONDS);
-            if (!countZero) {
+            boolean collectedFlushEvent =
+                    flushReceived.await(TIMEOUT_TOLERANCE_US, TimeUnit.MICROSECONDS);
+            if (!collectedFlushEvent) {
                 fail("Timed out waiting for flushCompleteEvent from " +
-                      registeredSensors.toString());
+                      registeredSensors.toString() + " waited for=" + timeToWaitUs/1000 + "ms");
             }
-            mSensorManager.unregisterListener(listener);
-            Log.i(TAG, "testBatchAndFlushWithMutipleSensors passed");
+            Log.i(TAG, "testBatchAndFlushWithMutipleSensors PASS'd");
         } finally {
+            mSensorManager.unregisterListener(listener);
             mWakeLock.release();
         }
     }
 
     private void assertSensorValues(Sensor sensor) {
-        assertTrue(sensor.getMaximumRange() >= 0);
-        assertTrue(sensor.getPower() >= 0);
-        assertTrue(sensor.getResolution() >= 0);
-        assertNotNull(sensor.getVendor());
-        assertTrue(sensor.getVersion() > 0);
+        assertTrue("Max range must be positive. Range=" + sensor.getMaximumRange()
+                + " " + sensor.getName(), sensor.getMaximumRange() >= 0);
+        assertTrue("Max power must be positive. Power=" + sensor.getPower() + " " +
+                sensor.getName(), sensor.getPower() >= 0);
+        assertTrue("Max resolution must be positive. Resolution=" + sensor.getResolution() +
+                " " + sensor.getName(), sensor.getResolution() >= 0);
+        assertNotNull("Vendor name must not be null " + sensor.getName(), sensor.getVendor());
+        assertTrue("Version must be positive version="  + sensor.getVersion() + " " +
+                    sensor.getName(), sensor.getVersion() > 0);
         int fifoMaxEventCount = sensor.getFifoMaxEventCount();
         int fifoReservedEventCount = sensor.getFifoReservedEventCount();
         assertTrue(fifoMaxEventCount >= 0);
         assertTrue(fifoReservedEventCount >= 0);
         assertTrue(fifoReservedEventCount <= fifoMaxEventCount);
         if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
-            assertTrue("One shot sensors should have zero FIFO Size",
+            assertTrue("One shot sensors should have zero FIFO Size " + sensor.getName(),
                     sensor.getFifoMaxEventCount() == 0);
-            assertTrue("One shot sensors should have zero FIFO Size",
+            assertTrue("One shot sensors should have zero FIFO Size "  + sensor.getName(),
                     sensor.getFifoReservedEventCount() == 0);
         }
     }
diff --git a/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java b/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
index 5e38842..586b9ef 100644
--- a/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
+++ b/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
@@ -335,11 +335,11 @@
     }
 
     /**
-     * Converts (interleaves) YUV420 planar to NV12 (if hw) or NV21 (if sw).
+     * Converts (interleaves) YUV420 planar to NV12.
      * Assumes packed, macroblock-aligned frame with no cropping
-     * (visible/coded row length == stride).  Swap U/V if |sw|.
+     * (visible/coded row length == stride).
      */
-    private static byte[] YUV420ToNV(int width, int height, byte[] yuv, boolean sw) {
+    private static byte[] YUV420ToNV(int width, int height, byte[] yuv) {
         byte[] nv = new byte[yuv.length];
         // Y plane we just copy.
         System.arraycopy(yuv, 0, nv, 0, width * height);
@@ -348,17 +348,9 @@
         int u_offset = width * height;
         int v_offset = u_offset + u_offset / 4;
         int nv_offset = width * height;
-        if (sw) {
-            for (int i = 0; i < width * height / 4; i++) {
-                nv[nv_offset++] = yuv[v_offset++];
-                nv[nv_offset++] = yuv[u_offset++];
-            }
-        }
-        else {
-            for (int i = 0; i < width * height / 4; i++) {
-                nv[nv_offset++] = yuv[u_offset++];
-                nv[nv_offset++] = yuv[v_offset++];
-            }
+        for (int i = 0; i < width * height / 4; i++) {
+            nv[nv_offset++] = yuv[u_offset++];
+            nv[nv_offset++] = yuv[v_offset++];
         }
         return nv;
     }
@@ -788,7 +780,7 @@
             // Convert YUV420 to NV12 if necessary
             if (mProperties.colorFormat != CodecCapabilities.COLOR_FormatYUV420Planar) {
                 return YUV420ToNV(mStreamParams.frameWidth, mStreamParams.frameHeight,
-                        mSrcFrame, mProperties.isGoogleSwCodec());
+                        mSrcFrame);
             } else {
                 return mSrcFrame;
             }
@@ -1390,7 +1382,7 @@
                     // Convert YUV420 to NV12 if necessary
                     if (properties.colorFormat != CodecCapabilities.COLOR_FormatYUV420Planar) {
                         srcFrame = YUV420ToNV(streamParams.frameWidth, streamParams.frameHeight,
-                                srcFrame, properties.isGoogleSwCodec());
+                                srcFrame);
                     }
                 }
 
@@ -1652,8 +1644,7 @@
                     // Convert YUV420 to NV12 if necessary
                     if (codecProperties[i].colorFormat !=
                             CodecCapabilities.COLOR_FormatYUV420Planar) {
-                        srcFrame[i] = YUV420ToNV(params.frameWidth, params.frameHeight, srcFrame[i],
-                                codecProperties[i].isGoogleSwCodec());
+                        srcFrame[i] = YUV420ToNV(params.frameWidth, params.frameHeight, srcFrame[i]);
                     }
                 }
 
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
index fb84509..dc5d30a 100644
--- a/tests/tests/tv/AndroidManifest.xml
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -81,6 +81,12 @@
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
             </intent-filter>
         </activity>
+        <activity android:name="android.tv.settings.cts.SettingsLeanbackStubActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java b/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
index 4a9e141..f5cc2e1 100644
--- a/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
@@ -133,7 +133,13 @@
             return;
         }
         for (final TvInputInfo info : mPassthroughInputList) {
-            mTvView.tune(info.getId(), TvContract.buildChannelUriForPassthroughInput(info.getId()));
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mTvView.tune(info.getId(),
+                            TvContract.buildChannelUriForPassthroughInput(info.getId()));
+                }
+            });
             mInstrumentation.waitForIdleSync();
             new PollingCheck(TIME_OUT) {
                 @Override
@@ -156,7 +162,13 @@
         for (int i = 0; i < mPassthroughInputList.size() * STRESS_FACTOR; ++i) {
             final TvInputInfo info =
                     mPassthroughInputList.get(random.nextInt(mPassthroughInputList.size()));
-            mTvView.tune(info.getId(), TvContract.buildChannelUriForPassthroughInput(info.getId()));
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mTvView.tune(info.getId(),
+                            TvContract.buildChannelUriForPassthroughInput(info.getId()));
+                }
+            });
             mInstrumentation.waitForIdleSync();
             if (random.nextBoolean()) {
                 new PollingCheck(TIME_OUT) {
diff --git a/tests/tests/tv/src/android/tv/settings/cts/SettingsLeanbackStubActivity.java b/tests/tests/tv/src/android/tv/settings/cts/SettingsLeanbackStubActivity.java
new file mode 100644
index 0000000..171f709
--- /dev/null
+++ b/tests/tests/tv/src/android/tv/settings/cts/SettingsLeanbackStubActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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 android.tv.settings.cts;
+
+import android.app.Activity;
+
+public class SettingsLeanbackStubActivity extends Activity {
+
+}
diff --git a/tests/tests/tv/src/android/tv/settings/cts/SettingsLeanbackTest.java b/tests/tests/tv/src/android/tv/settings/cts/SettingsLeanbackTest.java
new file mode 100644
index 0000000..c8b4a1b
--- /dev/null
+++ b/tests/tests/tv/src/android/tv/settings/cts/SettingsLeanbackTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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 android.tv.settings.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.util.List;
+
+public class SettingsLeanbackTest extends
+        ActivityInstrumentationTestCase2<SettingsLeanbackStubActivity> {
+
+    private static final String TAG = "SettingsLeanbackTest";
+
+    private static final String LEANBACK_SETTINGS_CATEGORY =
+            "android.intent.category.LEANBACK_SETTINGS";
+
+    private Activity mActivity;
+
+    public SettingsLeanbackTest() {
+        super(SettingsLeanbackStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mActivity = getActivity();
+        getInstrumentation().waitForIdleSync();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mActivity = null;
+        super.tearDown();
+    }
+
+    /**
+     * Test if there is at least one activity that can handle LEANBACK_LAUNCHER
+     * category intent.
+     *
+     * @throws Exception
+     */
+    public void testLeanbackLauncherIntentCategory() throws Exception {
+        if (!Utils.hasLeanback(mActivity)) {
+            return;
+        }
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER);
+        assertIntentCanBeHandled(intent);
+    }
+
+    /**
+     * Test if there is at least one activity that can handle LEANBACK_SETTINGS
+     * category intent.
+     *
+     * @throws Exception
+     */
+    public void testLeanbackSettingsIntentCategory() throws Exception {
+        if (!Utils.hasLeanback(mActivity)) {
+            return;
+        }
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(LEANBACK_SETTINGS_CATEGORY);
+        assertIntentCanBeHandled(intent);
+    }
+
+    private void assertIntentCanBeHandled(final Intent intent) {
+        PackageManager packageManager = getActivity().getPackageManager();
+        List<ResolveInfo> resolveInfoList =
+            packageManager.queryIntentActivities(intent, 0);
+        assertNotNull(resolveInfoList);
+        // one or more activity can handle this intent.
+        assertTrue(resolveInfoList.size() > 0);
+    }
+}
diff --git a/tests/tests/tv/src/android/tv/settings/cts/Utils.java b/tests/tests/tv/src/android/tv/settings/cts/Utils.java
new file mode 100644
index 0000000..95ce795
--- /dev/null
+++ b/tests/tests/tv/src/android/tv/settings/cts/Utils.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.tv.settings.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+public class Utils {
+    private Utils() { }
+
+    public static boolean hasFeature(Context context, String feature) {
+        return context.getPackageManager().hasSystemFeature(feature);
+    }
+
+    public static boolean hasLeanback(Context context) {
+        return hasFeature(context, PackageManager.FEATURE_LEANBACK);
+    }
+}