Merge "CTS Verifier - Request runtime permissions" into mnc-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index f743fb2..4d30dda 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -111,6 +111,7 @@
CtsVoiceInteractionService \
CtsVoiceInteractionApp \
CtsVoiceSettingsService \
+ CtsWidgetProviderApp \
$(cts_account_support_packages) \
$(cts_security_apps_list) \
$(cts_security_keysets_list)
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index e03ebdc..8e27e72 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -72,7 +72,7 @@
<action android:name="com.android.cts.managedprofile.ACTION_TEST_ALL_ACTIVITY" />
</intent-filter>
</activity>
- <activity android:name=".UserRestrictionActivity" >
+ <activity android:name=".SetPolicyActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT"/>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileWidgetPrimaryUserTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileWidgetPrimaryUserTest.java
new file mode 100644
index 0000000..bb1640b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileWidgetPrimaryUserTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2015 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.managedprofile;
+
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class contains tests for cross profile widget providers that are run on the primary users.
+ * The tests connect to a {@link android.appwidget.AppWidgetHost} and check whether the cross
+ * cross-profile widget can / cannot be found from the primary user.
+ * The tests cannot be run independently, but are part of one hostside test.
+ */
+public class CrossProfileWidgetPrimaryUserTest extends AndroidTestCase {
+ private static final String TAG = "CrossProfileWidgetPrimaryUserTest";
+
+ private static final int MSG_RESULT = 0;
+ private static final int MSG_PROVIDER_PRESENT = 1;
+ private static final int MSG_PROVIDER_UPDATES = 2;
+
+ private static final int RESULT_UNKNOWN = 0;
+ private static final int RESULT_PRESENT = 1;
+ private static final int RESULT_NOTPRESENT = 2;
+ private static final int RESULT_INTERRUPTED = 3;
+ private static final int RESULT_TIMEOUT = 4;
+
+ private static final String PACKAGE_EXTRA = "package-extra";
+
+ private Messenger mService;
+ private Connection mConnection;
+ private Result mResult;
+ private Messenger mResultMessenger;
+
+ @Override
+ protected void setUp() throws Exception {
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName(CrossProfileWidgetTest.WIDGET_PROVIDER_PKG,
+ CrossProfileWidgetTest.WIDGET_PROVIDER_PKG + ".SimpleAppWidgetHostService"));
+ mConnection = new Connection();
+ getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ mConnection.waitForService();
+ mResult = new Result(Looper.getMainLooper());
+ mResultMessenger = new Messenger(mResult);
+ }
+
+ public void testHasCrossProfileWidgetProvider_false() throws Exception {
+ int result = sendMessageToCallbacksService(MSG_PROVIDER_PRESENT,
+ CrossProfileWidgetTest.WIDGET_PROVIDER_PKG);
+ assertEquals(RESULT_NOTPRESENT, result);
+ }
+
+ public void testHostReceivesWidgetUpdates_false() throws Exception {
+ int result = sendMessageToCallbacksService(MSG_PROVIDER_UPDATES,
+ CrossProfileWidgetTest.WIDGET_PROVIDER_PKG);
+ assertEquals(RESULT_NOTPRESENT, result);
+ }
+
+ public void testHasCrossProfileWidgetProvider_true() throws Exception {
+ int result = sendMessageToCallbacksService(MSG_PROVIDER_PRESENT,
+ CrossProfileWidgetTest.WIDGET_PROVIDER_PKG);
+ assertEquals(RESULT_PRESENT, result);
+ }
+
+ public void testHostReceivesWidgetUpdates_true() throws Exception {
+ int result = sendMessageToCallbacksService(MSG_PROVIDER_UPDATES,
+ CrossProfileWidgetTest.WIDGET_PROVIDER_PKG);
+ assertEquals(RESULT_PRESENT, result);
+ }
+
+ private int sendMessageToCallbacksService(int msg, String packageName)
+ throws Exception {
+ Bundle params = new Bundle();
+ params.putString(PACKAGE_EXTRA, packageName);
+
+ Message message = Message.obtain(null, msg, params);
+ message.replyTo = mResultMessenger;
+
+ mService.send(message);
+
+ return mResult.waitForResult();
+ }
+
+ private static class Result extends Handler {
+
+ private final Semaphore mSemaphore = new Semaphore(0);
+ public int result = 0;
+
+ public Result(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_RESULT) {
+ result = msg.arg1;
+ mSemaphore.release();
+ } else {
+ super.handleMessage(msg);
+ }
+ }
+
+ public int waitForResult() {
+ try {
+ if (mSemaphore.tryAcquire(120, TimeUnit.SECONDS)) {
+ return result;
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted when talking to service", e);
+ }
+ return RESULT_TIMEOUT;
+ }
+ }
+
+ private class Connection implements ServiceConnection {
+ private final Semaphore mSemaphore = new Semaphore(0);
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mService = new Messenger(service);
+ mSemaphore.release();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ mService = null;
+ }
+
+ public void waitForService() {
+ try {
+ if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
+ return;
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted when connecting to service", e);
+ }
+ fail("failed to connect to service");
+ }
+ };
+
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileWidgetTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileWidgetTest.java
new file mode 100644
index 0000000..085d56f
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileWidgetTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 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.managedprofile;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class contains tests for cross profile widget providers that are run on the managed
+ * profile. Policies are set using {@link SetPolicyActivity} and then verified in these tests.
+ * The tests cannot be run independently, but are part of one hostside test.
+ */
+public class CrossProfileWidgetTest extends BaseManagedProfileTest {
+ static final String WIDGET_PROVIDER_PKG = "com.android.cts.widgetprovider";
+
+ private AppWidgetManager mAppWidgetManager;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mAppWidgetManager = (AppWidgetManager) mContext.getSystemService(Context.APPWIDGET_SERVICE);
+ }
+
+ /**
+ * This test checks that the widget provider was successfully whitelisted and verifies that
+ * if was added successfully and can be found inside the profile.
+ */
+ public void testCrossProfileWidgetProviderAdded() {
+ List<String> providers = mDevicePolicyManager.getCrossProfileWidgetProviders(
+ ADMIN_RECEIVER_COMPONENT);
+ assertEquals(1, providers.size());
+ assertTrue(providers.contains(WIDGET_PROVIDER_PKG));
+ // check that widget can be found inside the profile
+ assertTrue(containsWidgetProviderPkg(mAppWidgetManager.getInstalledProviders()));
+ }
+
+ /**
+ * This test verifies that the widget provider was successfully removed from the whitelist.
+ */
+ public void testCrossProfileWidgetProviderRemoved() {
+ List<String> providers = mDevicePolicyManager.getCrossProfileWidgetProviders(
+ ADMIN_RECEIVER_COMPONENT);
+ assertTrue(providers.isEmpty());
+ // check that widget can still be found inside the profile
+ // This does not currently work correctly: http://b/issues/21180997
+ // assertTrue(containsWidgetProviderPkg(mAppWidgetManager.getInstalledProviders()));
+ }
+
+ private boolean containsWidgetProviderPkg(List<AppWidgetProviderInfo> widgets) {
+ for (AppWidgetProviderInfo widget : widgets) {
+ if (WIDGET_PROVIDER_PKG.equals(widget.provider.getPackageName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/UserRestrictionActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/SetPolicyActivity.java
similarity index 60%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/UserRestrictionActivity.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/SetPolicyActivity.java
index e8decf8..f59363b 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/UserRestrictionActivity.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/SetPolicyActivity.java
@@ -28,15 +28,18 @@
/**
* Simple activity that adds or clears a user restriction depending on the value of the extras.
*/
-public class UserRestrictionActivity extends Activity {
+public class SetPolicyActivity extends Activity {
- private static final String TAG = UserRestrictionActivity.class.getName();
+ private static final String TAG = SetPolicyActivity.class.getName();
private static final String EXTRA_RESTRICTION_KEY = "extra-restriction-key";
+ private static final String EXTRA_PACKAGE_NAME = "extra-package-name";
private static final String EXTRA_COMMAND = "extra-command";
- private static final String ADD_COMMAND = "add-restriction";
- private static final String CLEAR_COMMAND = "clear-restriction";
+ private static final String ADD_RESTRICTION_COMMAND = "add-restriction";
+ private static final String CLEAR_RESTRICTION_COMMAND = "clear-restriction";
+ private static final String ADD_CROSS_PROFILE_WIDGET_COMMAND = "add-cross-profile-widget";
+ private static final String REMOVE_CROSS_PROFILE_WIDGET_COMMAND = "remove-cross-profile-widget";
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -62,19 +65,32 @@
private void handleIntent(Intent intent) {
DevicePolicyManager dpm = (DevicePolicyManager)
getSystemService(Context.DEVICE_POLICY_SERVICE);
- String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION_KEY);
String command = intent.getStringExtra(EXTRA_COMMAND);
- Log.i(TAG, "Command: \"" + command + "\". Restriction: \"" + restrictionKey + "\"");
+ Log.i(TAG, "Command: \"" + command);
- if (ADD_COMMAND.equals(command)) {
+ if (ADD_RESTRICTION_COMMAND.equals(command)) {
+ String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION_KEY);
dpm.addUserRestriction(BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT, restrictionKey);
Log.i(TAG, "Added user restriction " + restrictionKey
+ " for user " + Process.myUserHandle());
- } else if (CLEAR_COMMAND.equals(command)) {
+ } else if (CLEAR_RESTRICTION_COMMAND.equals(command)) {
+ String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION_KEY);
dpm.clearUserRestriction(
BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT, restrictionKey);
Log.i(TAG, "Cleared user restriction " + restrictionKey
+ " for user " + Process.myUserHandle());
+ } else if (ADD_CROSS_PROFILE_WIDGET_COMMAND.equals(command)) {
+ String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+ dpm.addCrossProfileWidgetProvider(BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT,
+ packageName);
+ Log.i(TAG, "Added cross-profile widget provider for package:" + packageName
+ + " for user " + Process.myUserHandle());
+ } else if (REMOVE_CROSS_PROFILE_WIDGET_COMMAND.equals(command)) {
+ String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+ dpm.removeCrossProfileWidgetProvider(BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT,
+ packageName);
+ Log.i(TAG, "Removed cross-profile widget provider for package:" + packageName
+ + " for user " + Process.myUserHandle());
} else {
Log.e(TAG, "Invalid command: " + command);
}
diff --git a/hostsidetests/devicepolicy/app/WidgetProvider/Android.mk b/hostsidetests/devicepolicy/app/WidgetProvider/Android.mk
new file mode 100644
index 0000000..c0e35fa
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/WidgetProvider/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2015 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_PACKAGE_NAME := CtsWidgetProviderApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/WidgetProvider/AndroidManifest.xml b/hostsidetests/devicepolicy/app/WidgetProvider/AndroidManifest.xml
new file mode 100644
index 0000000..77246b5
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/WidgetProvider/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2015 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.widgetprovider">
+
+ <uses-permission android:name="android.permission.BIND_APPWIDGET"/>
+
+ <application>
+ <receiver android:name="SimpleWidgetProvider" >
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/simple_widget_provider_info" />
+ </receiver>
+ <service android:name=".SimpleAppWidgetHostService" >
+ <intent-filter>
+ <action android:name="com.android.cts.widgetprovider.REGISTER_CALLBACK" />
+ </intent-filter>
+ </service>
+ </application>
+
+</manifest>
+
diff --git a/hostsidetests/devicepolicy/app/WidgetProvider/res/layout/simple_widget.xml b/hostsidetests/devicepolicy/app/WidgetProvider/res/layout/simple_widget.xml
new file mode 100644
index 0000000..338c57a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/WidgetProvider/res/layout/simple_widget.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2015 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="horizontal"
+ android:padding="10dip">
+
+ <ImageView
+ android:id="@+id/pkg_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+
+ <TextView
+ android:id="@+id/pkg_name"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:paddingLeft="10dip"
+ android:layout_toRightOf="@id/pkg_icon"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/WidgetProvider/res/xml/simple_widget_provider_info.xml b/hostsidetests/devicepolicy/app/WidgetProvider/res/xml/simple_widget_provider_info.xml
new file mode 100644
index 0000000..8a002d2
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/WidgetProvider/res/xml/simple_widget_provider_info.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="40dp"
+ android:minHeight="40dp"
+ android:resizeMode="horizontal|vertical"
+ android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/WidgetProvider/src/com/android/cts/widgetprovider/SimpleAppWidgetHostService.java b/hostsidetests/devicepolicy/app/WidgetProvider/src/com/android/cts/widgetprovider/SimpleAppWidgetHostService.java
new file mode 100644
index 0000000..b5b6003
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/WidgetProvider/src/com/android/cts/widgetprovider/SimpleAppWidgetHostService.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2015 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.widgetprovider;
+
+import android.app.Service;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Service that acts as AppWidgetHost that listens to onProvidersChanged callbacks and updates the
+ * internally stored list of profile widgets. The service reacts to messages sent from the device
+ * side tests and returns whether the expected widget provider is currently present or not.
+ */
+public class SimpleAppWidgetHostService extends Service {
+ private static final String TAG = "SimpleAppWidgetHostService";
+
+ private static final int MSG_RESULT = 0;
+ private static final int MSG_PROVIDER_PRESENT = 1;
+ private static final int MSG_PROVIDER_UPDATES = 2;
+
+ private static final int RESULT_UNKNOWN = 0;
+ private static final int RESULT_PRESENT = 1;
+ private static final int RESULT_NOT_PRESENT = 2;
+ private static final int RESULT_INTERRUPTED = 3;
+ private static final int RESULT_TIMEOUT = 4;
+
+ public static final String USER_EXTRA = "user-extra";
+ public static final String PACKAGE_EXTRA = "package-extra";
+ public static final String REPLY_EXTRA = "reply-extra";
+
+ private AppWidgetManager mAppWidgetManager;
+ private SimpleAppWidgetHost mAppWidgetHost;
+ private SimpleAppWidgetHostView mAppWidgetHostView;
+ private int mAppWidgetId;
+ private Messenger mMessenger;
+ private UserHandle mUserHandle;
+ private Semaphore mWidgetUpdateSemaphore = new Semaphore(0);
+ private RemoteViews mRemoteViews;
+
+ class CheckHandler extends Handler {
+ public CheckHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ Bundle params = null;
+ if (msg.obj instanceof Bundle) {
+ params = (Bundle) (msg.obj);
+ }
+ try {
+ switch (msg.what) {
+ case MSG_PROVIDER_PRESENT: {
+ Log.d(TAG, "MSG_PROVIDER_PRESENT");
+ int result = RESULT_UNKNOWN;
+ try {
+ AppWidgetProviderInfo info = mAppWidgetHost.getProvider(params);
+ result = (info != null) ? RESULT_PRESENT : RESULT_NOT_PRESENT;
+ if (info != null) {
+ bindAppWidget(info);
+ }
+ } catch (InterruptedException e) {
+ result = RESULT_INTERRUPTED;
+ }
+ msg.replyTo.send(Message.obtain(null, MSG_RESULT, result
+ , 0 /* not used */));
+ break;
+ }
+ case MSG_PROVIDER_UPDATES: {
+ Log.d(TAG, "MSG_PROVIDER_UPDATES");
+ int result = RESULT_UNKNOWN;
+ try {
+ updateWidgetViaWidgetId();
+ boolean update = waitForUpdate();
+ result = update ? RESULT_PRESENT : RESULT_NOT_PRESENT;
+ } catch (InterruptedException e) {
+ result = RESULT_INTERRUPTED;
+ }
+ msg.replyTo.send(Message.obtain(null, MSG_RESULT, result
+ , 0 /* not used */));
+ break;
+ }
+ default:
+ super.handleMessage(msg);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to report test status");
+ }
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent == null) {
+ return START_NOT_STICKY;
+ }
+ if ("com.android.cts.widgetprovider.REGISTER_CALLBACK".equals(intent.getAction())) {
+ mUserHandle = getUserHandleArgument(this, USER_EXTRA, intent);
+ Log.d(TAG, "mUserHandle=" + mUserHandle);
+ setup();
+ }
+ return START_STICKY;
+ }
+
+ private void setup() {
+ HandlerThread handlerThread = new HandlerThread("Widget test callback handler");
+ handlerThread.start();
+ mMessenger = new Messenger(new CheckHandler(handlerThread.getLooper()));
+ mAppWidgetManager = (AppWidgetManager) getSystemService(Context.APPWIDGET_SERVICE);
+ mAppWidgetHost = new SimpleAppWidgetHost(this, 0);
+ mAppWidgetHost.deleteHost();
+ mAppWidgetHost.startListening();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+
+ @Override
+ public void onDestroy() {
+ mAppWidgetHost.stopListening();
+ mAppWidgetHost.deleteAppWidgetId(mAppWidgetId);
+ mAppWidgetHost.deleteHost();
+ }
+
+ private void bindAppWidget(AppWidgetProviderInfo info) {
+ mAppWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ Log.d(TAG, "Registering app widget with id:" + mAppWidgetId);
+ mAppWidgetManager.bindAppWidgetIdIfAllowed(mAppWidgetId, mUserHandle, info.provider, null);
+ mAppWidgetHostView = (SimpleAppWidgetHostView) mAppWidgetHost.createView(this,
+ mAppWidgetId, info);
+ mRemoteViews = new RemoteViews(info.provider.getPackageName(), R.layout.simple_widget);
+ }
+
+ private UserHandle getUserHandleArgument(Context context, String key,
+ Intent intent) {
+ UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
+ int serial = intent.getIntExtra(key, 0);
+ Log.d(TAG, "userId=" + serial);
+ return um.getUserForSerialNumber(serial);
+ }
+
+ private void updateWidgetViaWidgetId() {
+ Log.d(TAG, "Forcing widget update via widget id");
+ mWidgetUpdateSemaphore.drainPermits();
+ // trigger a widget update
+ mAppWidgetManager.updateAppWidget(mAppWidgetId, mRemoteViews);
+ }
+
+ private boolean waitForUpdate() throws InterruptedException {
+ // wait for updateAppWidget to arrive
+ return mWidgetUpdateSemaphore.tryAcquire(20, TimeUnit.SECONDS);
+ }
+
+ private class SimpleAppWidgetHost extends AppWidgetHost {
+ private List<AppWidgetProviderInfo> mProviders;
+ private Semaphore mSemaphore = new Semaphore(0);
+ public SimpleAppWidgetHost(Context context, int hostId) {
+ super(context, hostId);
+ synchronized (this) {
+ mProviders = mAppWidgetManager.getInstalledProvidersForProfile(mUserHandle);
+ }
+ }
+
+ @Override
+ protected void onProvidersChanged() {
+ super.onProvidersChanged();
+ Log.d(TAG, "onProvidersChanged callback received");
+ synchronized (this) {
+ mProviders = mAppWidgetManager.getInstalledProvidersForProfile(mUserHandle);
+ }
+ mSemaphore.release();
+ }
+
+ @Override
+ protected AppWidgetHostView onCreateView(Context context, int id, AppWidgetProviderInfo info) {
+ return new SimpleAppWidgetHostView(context);
+ }
+
+ public AppWidgetProviderInfo getProvider(Bundle params) throws InterruptedException {
+ String packageName = params.getString(PACKAGE_EXTRA);
+ while (mSemaphore.tryAcquire(30, TimeUnit.SECONDS)) {
+ mSemaphore.drainPermits();
+ Log.d(TAG, "checking for " + packageName + " " + mUserHandle);
+ synchronized (this) {
+ for (AppWidgetProviderInfo providerInfo : mProviders) {
+ if (providerInfo.provider.getPackageName().equals(packageName)) {
+ Log.d(TAG, "Provider exists " + packageName
+ + " for user " + mUserHandle);
+ return providerInfo;
+ }
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ private class SimpleAppWidgetHostView extends AppWidgetHostView {
+ public SimpleAppWidgetHostView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void updateAppWidget(RemoteViews views) {
+ super.updateAppWidget(views);
+ Log.d(TAG, "Host view received widget update");
+ mWidgetUpdateSemaphore.release();
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/WidgetProvider/src/com/android/cts/widgetprovider/SimpleWidgetProvider.java b/hostsidetests/devicepolicy/app/WidgetProvider/src/com/android/cts/widgetprovider/SimpleWidgetProvider.java
new file mode 100644
index 0000000..3bd4530
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/WidgetProvider/src/com/android/cts/widgetprovider/SimpleWidgetProvider.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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.widgetprovider;
+
+import android.appwidget.AppWidgetProvider;
+
+/**
+ * A simple widget provider to test cross-profile widgets.
+ */
+public class SimpleWidgetProvider extends AppWidgetProvider {}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 671c3e5..650e963 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -185,9 +185,6 @@
protected boolean runDeviceTestsAsUser(
String pkgName, @Nullable String testClassName, String testMethodName, int userId)
throws DeviceNotAvailableException {
- if (testClassName.startsWith(".")) {
- testClassName = pkgName + testClassName;
- }
return runDeviceTests(pkgName, testClassName, testMethodName, userId);
}
@@ -200,6 +197,9 @@
protected boolean runDeviceTests(String pkgName, @Nullable String testClassName,
@Nullable String testMethodName, @Nullable Integer userId, @Nullable String params)
throws DeviceNotAvailableException {
+ if (testClassName != null && testClassName.startsWith(".")) {
+ testClassName = pkgName + testClassName;
+ }
TestRunResult runResult = (userId == null && params == null)
? doRunTests(pkgName, testClassName, testMethodName)
: doRunTestsAsUser(pkgName, testClassName, testMethodName,
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 93e1aff..acc5b26 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -48,6 +48,9 @@
private static final String WIFI_CONFIG_CREATOR_PKG = "com.android.cts.wificonfigcreator";
private static final String WIFI_CONFIG_CREATOR_APK = "CtsWifiConfigCreator.apk";
+ private static final String WIDGET_PROVIDER_APK = "CtsWidgetProviderApp.apk";
+ private static final String WIDGET_PROVIDER_PKG = "com.android.cts.widgetprovider";
+
private static final String ADMIN_RECEIVER_TEST_CLASS =
MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
@@ -596,6 +599,47 @@
"testNfcShareEnabled", 0));
}
+ public void testCrossProfileWidgets() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ try {
+ installApp(WIDGET_PROVIDER_APK);
+ getDevice().executeShellCommand("appwidget grantbind --user 0 --package "
+ + WIDGET_PROVIDER_PKG);
+ startWidgetHostService();
+
+ String commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+ "add-cross-profile-widget", mUserId);
+ assertTrue("Command was expected to succeed " + commandOutput,
+ commandOutput.contains("Status: ok"));
+
+ assertTrue(runDeviceTests(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
+ "testCrossProfileWidgetProviderAdded", mUserId));
+ assertTrue(runDeviceTests(MANAGED_PROFILE_PKG, ".CrossProfileWidgetPrimaryUserTest",
+ "testHasCrossProfileWidgetProvider_true", 0));
+ assertTrue(runDeviceTests(MANAGED_PROFILE_PKG, ".CrossProfileWidgetPrimaryUserTest",
+ "testHostReceivesWidgetUpdates_true", 0));
+
+ commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+ "remove-cross-profile-widget", mUserId);
+ assertTrue("Command was expected to succeed " + commandOutput,
+ commandOutput.contains("Status: ok"));
+
+ assertTrue(runDeviceTests(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
+ "testCrossProfileWidgetProviderRemoved", mUserId));
+ assertTrue(runDeviceTests(MANAGED_PROFILE_PKG, ".CrossProfileWidgetPrimaryUserTest",
+ "testHasCrossProfileWidgetProvider_false", 0));
+ assertTrue(runDeviceTests(MANAGED_PROFILE_PKG, ".CrossProfileWidgetPrimaryUserTest",
+ "testHostReceivesWidgetUpdates_false", 0));
+ } finally {
+ changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG, "remove-cross-profile-widget",
+ mUserId);
+ getDevice().uninstallPackage(WIDGET_PROVIDER_PKG);
+ }
+ }
+
private void disableActivityForUser(String activityName, int userId)
throws DeviceNotAvailableException {
String command = "am start -W --user " + userId
@@ -612,7 +656,20 @@
+ " -c android.intent.category.DEFAULT "
+ " --es extra-command " + command
+ " --es extra-restriction-key " + key
- + " " + MANAGED_PROFILE_PKG + "/.UserRestrictionActivity";
+ + " " + MANAGED_PROFILE_PKG + "/.SetPolicyActivity";
+ String commandOutput = getDevice().executeShellCommand(adbCommand);
+ CLog.logAndDisplay(LogLevel.INFO,
+ "Output for command " + adbCommand + ": " + commandOutput);
+ return commandOutput;
+ }
+
+ private String changeCrossProfileWidgetForUser(String packageName, String command, int userId)
+ throws DeviceNotAvailableException {
+ String adbCommand = "am start -W --user " + userId
+ + " -c android.intent.category.DEFAULT "
+ + " --es extra-command " + command
+ + " --es extra-package-name " + packageName
+ + " " + MANAGED_PROFILE_PKG + "/.SetPolicyActivity";
String commandOutput = getDevice().executeShellCommand(adbCommand);
CLog.logAndDisplay(LogLevel.INFO,
"Output for command " + adbCommand + ": " + commandOutput);
@@ -627,6 +684,15 @@
+ getDevice().executeShellCommand(command));
}
+ protected void startWidgetHostService() throws Exception {
+ String command = "am startservice --user 0 "
+ + "-a " + WIDGET_PROVIDER_PKG + ".REGISTER_CALLBACK "
+ + "--ei user-extra " + getUserSerialNumber(mUserId)
+ + " " + WIDGET_PROVIDER_PKG + "/.SimpleAppWidgetHostService";
+ CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": "
+ + getDevice().executeShellCommand(command));
+ }
+
private void assertAppLinkResult(String methodName) throws DeviceNotAvailableException {
assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".AppLinkTest", methodName, mUserId));
}
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
index 0589792..a787cdd 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
@@ -349,7 +349,12 @@
continue;
}
- String[] parts = line.split(",");
+
+ // With a default limit of 0, empty strings at the end are discarded.
+ // We still consider the empty string as a valid value in some cases.
+ // Using any negative number for the limit will preserve a trailing empty string.
+ // @see String#split(String, int)
+ String[] parts = line.split(",", -1);
assertInteger(parts[0]); // old version
assertInteger(parts[1]); // UID
switch (parts[2]) { // aggregation type
diff --git a/tests/tests/assist/AndroidManifest.xml b/tests/tests/assist/AndroidManifest.xml
index b6cd684..97bd874 100644
--- a/tests/tests/assist/AndroidManifest.xml
+++ b/tests/tests/assist/AndroidManifest.xml
@@ -29,6 +29,7 @@
<action android:name="android.intent.action.TEST_START_ACTIVITY_ASSIST_STRUCTURE" />
<action android:name="android.intent.action.TEST_START_ACTIVITY_DISABLE_CONTEXT" />
<action android:name="android.intent.action.TEST_START_ACTIVITY_FLAG_SECURE" />
+ <action android:name="android.intent.action.TEST_START_ACTIVITY_LIFECYCLE" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
diff --git a/tests/tests/assist/common/src/android/assist/common/Utils.java b/tests/tests/assist/common/src/android/assist/common/Utils.java
index bf051c0..8d555da 100644
--- a/tests/tests/assist/common/src/android/assist/common/Utils.java
+++ b/tests/tests/assist/common/src/android/assist/common/Utils.java
@@ -24,8 +24,11 @@
public class Utils {
public static final String TESTCASE_TYPE = "testcase_type";
public static final String TESTINFO = "testinfo";
- public static final String BROADCAST_INTENT = "android.intent.action.ASSIST_TESTAPP";
- public static final String BROADCAST_ASSIST_DATA_INTENT = "android.intent.action.ASSIST_DATA";
+ public static final String ACTION_PREFIX = "android.intent.action.";
+ public static final String BROADCAST_INTENT = ACTION_PREFIX + "ASSIST_TESTAPP";
+ public static final String BROADCAST_ASSIST_DATA_INTENT = ACTION_PREFIX + "ASSIST_DATA";
+ public static final String BROADCAST_INTENT_START_ASSIST = ACTION_PREFIX + "START_ASSIST";
+ public static final String ASSIST_RECEIVER_REGISTERED = ACTION_PREFIX + "ASSIST_READY";
public static final String TEST_ERROR = "Error In Test:";
public static final String ASSIST_STRUCTURE_KEY = "assist_structure";
@@ -33,12 +36,30 @@
public static final String ASSIST_BUNDLE_KEY = "assist_bundle";
public static final String ASSIST_SCREENSHOT_KEY = "assist_screenshot";
+
+ /** Lifecycle Test intent constants */
+ public static final String LIFECYCLE_PREFIX = ACTION_PREFIX + "lifecycle_";
+ public static final String LIFECYCLE_HASRESUMED = LIFECYCLE_PREFIX + "hasResumed";
+ public static final String LIFECYCLE_ONPAUSE = LIFECYCLE_PREFIX + "onpause";
+ public static final String LIFECYCLE_ONSTOP = LIFECYCLE_PREFIX + "onstop";
+ public static final String LIFECYCLE_ONDESTROY = LIFECYCLE_PREFIX + "ondestroy";
+
+ /** Flag Secure Test intent constants */
+ public static final String FLAG_SECURE_HASRESUMED = ACTION_PREFIX + "flag_secure_hasResumed";
+
+ /** Two second timeout for getting back assist context */
public static final int TIMEOUT_MS = 2 * 1000; // TODO(awlee): what is the timeout
+ public static final int ACTIVITY_ONRESUME_TIMEOUT_MS = 4000;
+ public static final String EXTRA_REGISTER_RECEIVER = "register_receiver";
/** Test name suffixes */
public static final String ASSIST_STRUCTURE = "ASSIST_STRUCTURE";
public static final String DISABLE_CONTEXT = "DISABLE_CONTEXT";
public static final String FLAG_SECURE = "FLAG_SECURE";
+ public static final String LIFECYCLE = "LIFECYCLE";
+
+ /** Session intent constants */
+ public static final String HIDE_SESSION = "android.intent.action.hide_session";
/**
* The shim activity that starts the service associated with each test.
@@ -46,10 +67,12 @@
public static final String getTestActivity(String testCaseType) {
switch (testCaseType) {
case ASSIST_STRUCTURE:
- case FLAG_SECURE:
return "service.AssistStructureActivity";
case DISABLE_CONTEXT:
return "service.DisableContextActivity";
+ case FLAG_SECURE:
+ case LIFECYCLE:
+ return "service.DelayedAssistantActivity";
default:
return "";
}
@@ -67,6 +90,9 @@
case FLAG_SECURE:
return new ComponentName(
"android.assist.testapp", "android.assist.testapp.SecureActivity");
+ case LIFECYCLE:
+ return new ComponentName(
+ "android.assist.testapp", "android.assist.testapp.LifecycleActivity");
default:
return new ComponentName("","");
}
diff --git a/tests/tests/assist/service/AndroidManifest.xml b/tests/tests/assist/service/AndroidManifest.xml
index 2c5206a..cdbeef0 100644
--- a/tests/tests/assist/service/AndroidManifest.xml
+++ b/tests/tests/assist/service/AndroidManifest.xml
@@ -34,7 +34,6 @@
<activity android:name=".AssistStructureActivity" >
<intent-filter>
<action android:name="android.intent.action.START_TEST_ASSIST_STRUCTURE" />
- <action android:name="android.intent.action.START_TEST_FLAG_SECURE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
@@ -45,6 +44,14 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+ <activity android:name=".DelayedAssistantActivity"
+ android:label="Delay Assistant Start Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.START_TEST_LIFECYCLE" />
+ <action android:name="android.intent.action.START_TEST_FLAG_SECURE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
<service android:name=".MainInteractionSessionService"
android:permission="android.permission.BIND_VOICE_INTERACTION"
android:process=":session">
diff --git a/tests/tests/assist/service/res/layout/assist_layer.xml b/tests/tests/assist/service/res/layout/assist_layer.xml
new file mode 100644
index 0000000..49f35c9
--- /dev/null
+++ b/tests/tests/assist/service/res/layout/assist_layer.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/assist_layer"
+ android:layout_width="match_parent"
+ android:background="@color/assist_layer_background"
+ android:layout_height="match_parent">
+ <TextView
+ android:layout_centerInParent="true"
+ android:text="@string/test_assistant_text"
+ android:textColor="@android:color/white"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</RelativeLayout>
diff --git a/tests/tests/assist/service/res/values/colors.xml b/tests/tests/assist/service/res/values/colors.xml
new file mode 100644
index 0000000..4423140
--- /dev/null
+++ b/tests/tests/assist/service/res/values/colors.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="assist_layer_background">#aa000000</color>
+</resources>
diff --git a/tests/tests/assist/service/res/values/strings.xml b/tests/tests/assist/service/res/values/strings.xml
new file mode 100644
index 0000000..ea959e6
--- /dev/null
+++ b/tests/tests/assist/service/res/values/strings.xml
@@ -0,0 +1,18 @@
+<!--
+ Copyright 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. -->
+
+<resources>
+ <string name="test_assistant_text">I am an Assistant</string>
+</resources>
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java b/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
new file mode 100644
index 0000000..31d1694
--- /dev/null
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
@@ -0,0 +1,38 @@
+/*
+ * 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.assist.service;
+
+import android.app.Activity;
+import android.assist.common.Utils;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.util.Log;
+
+public class DelayedAssistantActivity extends Activity {
+ static final String TAG = "DelatyedAssistantActivity";
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(this, MainInteractionService.class));
+ intent.putExtra(Utils.EXTRA_REGISTER_RECEIVER, true);
+ finish();
+ ComponentName serviceName = startService(intent);
+ Log.i(TAG, "Started service: " + serviceName);
+ }
+}
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
index 85bd6ea..7530933 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
@@ -16,8 +16,15 @@
package android.assist.service;
+import static android.service.voice.VoiceInteractionSession.SHOW_WITH_ASSIST;
+import static android.service.voice.VoiceInteractionSession.SHOW_WITH_SCREENSHOT;
+
+import android.assist.common.Utils;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.os.Bundle;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
@@ -27,6 +34,7 @@
static final String TAG = "MainInteractionService";
private Intent mIntent;
private boolean mReady = false;
+ private BroadcastReceiver mBroadcastReceiver;
@Override
public void onReady() {
@@ -36,7 +44,7 @@
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i(TAG, "onStartCommand received");
+ Log.i(TAG, "onStartCommand received - intent: " + intent);
mIntent = intent;
maybeStart();
return START_NOT_STICKY;
@@ -47,14 +55,40 @@
Log.wtf(TAG, "Can't start session because either intent is null or onReady() "
+ "has not been called yet. mIntent = " + mIntent + ", mReady = " + mReady);
} else {
- Log.i(TAG, "Yay! about to start session");
if (isActiveService(this, new ComponentName(this, getClass()))) {
- showSession(new Bundle(), VoiceInteractionSession.SHOW_WITH_ASSIST |
- VoiceInteractionSession.SHOW_WITH_SCREENSHOT);
+ if (mIntent.getBooleanExtra(Utils.EXTRA_REGISTER_RECEIVER, false)) {
+ Log.i(TAG, "Registering receiver to start session later");
+ if (mBroadcastReceiver == null) {
+ mBroadcastReceiver = new MainInteractionServiceBroadcastReceiver();
+ registerReceiver(mBroadcastReceiver,
+ new IntentFilter(Utils.BROADCAST_INTENT_START_ASSIST));
+ }
+ sendBroadcast(new Intent(Utils.ASSIST_RECEIVER_REGISTERED));
+ } else {
+ Log.i(TAG, "Yay! about to start session");
+ showSession(new Bundle(), VoiceInteractionSession.SHOW_WITH_ASSIST |
+ VoiceInteractionSession.SHOW_WITH_SCREENSHOT);
+ }
} else {
Log.wtf(TAG, "**** Not starting MainInteractionService because" +
" it is not set as the current voice interaction service");
}
}
}
+
+ private class MainInteractionServiceBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Utils.BROADCAST_INTENT_START_ASSIST)) {
+ showSession(new Bundle(), SHOW_WITH_ASSIST | SHOW_WITH_SCREENSHOT);
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mBroadcastReceiver != null) {
+ unregisterReceiver(mBroadcastReceiver);
+ }
+ }
}
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
index 9a835c2..f297b3e 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
@@ -18,13 +18,17 @@
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
+import android.assist.service.R;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.service.voice.VoiceInteractionSession;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
import java.io.ByteArrayOutputStream;
@@ -39,6 +43,7 @@
private boolean hasReceivedAssistData = false;
private boolean hasReceivedScreenshot = false;
+ private BroadcastReceiver mReceiver;
MainInteractionSession(Context context) {
super(context);
@@ -48,12 +53,27 @@
@Override
public void onCreate() {
super.onCreate();
+ mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.HIDE_SESSION)) {
+ hide();
+ }
+ }
+ };
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.HIDE_SESSION);
+ mContext.registerReceiver(mReceiver, filter);
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy()");
super.onDestroy();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
}
@Override
@@ -115,6 +135,15 @@
}
}
+ @Override
+ public View onCreateContentView() {
+ LayoutInflater f = getLayoutInflater();
+ if (f == null) {
+ Log.wtf(TAG, "layout inflater was null");
+ }
+ return f.inflate(R.layout.assist_layer,null);
+ }
+
class DoneReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
index bdf3d4a..a7e7087 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -48,7 +48,7 @@
protected BroadcastReceiver mReceiver;
protected Bundle mAssistBundle;
protected Context mContext;
- protected CountDownLatch mLatch;
+ protected CountDownLatch mLatch, mAssistantReadyLatch;
private String mTestName;
public AssistTestBase() {
@@ -58,6 +58,7 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+ mAssistantReadyLatch = new CountDownLatch(1);
mContext = getInstrumentation().getTargetContext();
SystemUtil.runShellCommand(getInstrumentation(),
"settings put secure assist_structure_enabled 1");
@@ -71,6 +72,7 @@
mContext.unregisterReceiver(mReceiver);
mTestActivity.finish();
super.tearDown();
+ mContext.sendBroadcast(new Intent(Utils.HIDE_SESSION));
}
protected void startTestActivity(String testName) {
@@ -84,9 +86,32 @@
}
/**
+ * Called when waiting for Assistant's Broadcast Receiver to be setup
+ */
+ public void waitForAssistantToBeReady() throws Exception {
+ Log.i(TAG, "waiting for assistant to be ready before continuing");
+ if (!mAssistantReadyLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Assistant was not ready before timeout of: " + Utils.TIMEOUT_MS + "msec");
+ }
+ }
+
+ /**
+ * Send broadcast to MainInteractionService to start a session
+ */
+ protected void startSession() {
+ mContext.sendBroadcast(new Intent(Utils.BROADCAST_INTENT_START_ASSIST));
+ }
+
+ /**
* Called after startTestActivity
*/
protected boolean waitForBroadcast() throws Exception {
+ mTestActivity.start3pApp(mTestName);
+ mTestActivity.startTest(mTestName);
+ return waitForContext();
+ }
+
+ protected boolean waitForContext() throws Exception {
mLatch = new CountDownLatch(1);
if (mReceiver != null) {
mContext.unregisterReceiver(mReceiver);
@@ -95,8 +120,6 @@
mContext.registerReceiver(mReceiver,
new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
- mTestActivity.start3pApp(mTestName);
- mTestActivity.startTest(mTestName);
if (!mLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
fail("Failed to receive broadcast in " + Utils.TIMEOUT_MS + "msec");
return false;
@@ -188,4 +211,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
index 04bea77..9b29407 100644
--- a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
+++ b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
@@ -97,4 +97,4 @@
verifyAssistDataNullness(true, true, true, true);
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
index fc14900..40bf7a7 100644
--- a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
@@ -17,8 +17,14 @@
package android.assist.cts;
import android.assist.common.Utils;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Test we receive proper assist data (root assistStructure with no children) when the assistant is
@@ -26,10 +32,13 @@
*/
public class FlagSecureTest extends AssistTestBase {
- static final String TAG = "DisableContextTest";
+ static final String TAG = "FlagSecureTest";
private static final String TEST_CASE_TYPE = Utils.FLAG_SECURE;
+ private BroadcastReceiver mReceiver;
+ private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+
public FlagSecureTest() {
super();
}
@@ -37,13 +46,35 @@
@Override
public void setUp() throws Exception {
super.setUp();
+ setUpAndRegisterReceiver();
startTestActivity(TEST_CASE_TYPE);
- waitForBroadcast();
}
@Override
public void tearDown() throws Exception {
super.tearDown();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new FlagSecureTestBroadcastReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.FLAG_SECURE_HASRESUMED);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private void waitForOnResume() throws Exception {
+ Log.i(TAG, "waiting for onResume() before continuing");
+ if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+ }
}
public void testSecureActivity() throws Exception {
@@ -52,4 +83,16 @@
// verify that we have only the root window and not its children.
verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), true);
}
-}
\ No newline at end of file
+
+ private class FlagSecureTestBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.FLAG_SECURE_HASRESUMED)) {
+ mHasResumedLatch.countDown();
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+ mAssistantReadyLatch.countDown();
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
new file mode 100644
index 0000000..19a1be5
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 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.assist.cts;
+
+import android.assist.TestStartActivity;
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.cts.util.SystemUtil;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/** Test we receive proper assist data when context is disabled or enabled */
+
+public class LifecycleTest extends AssistTestBase {
+ private static final String TAG = "LifecycleTest";
+ private static final String action_hasResumed = Utils.LIFECYCLE_HASRESUMED;
+ private static final String action_onPause = Utils.LIFECYCLE_ONPAUSE;
+ private static final String action_onStop = Utils.LIFECYCLE_ONSTOP;
+ private static final String action_onDestroy = Utils.LIFECYCLE_ONDESTROY;
+
+ private BroadcastReceiver mLifecycleTestBroadcastReceiver;
+ private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+ private CountDownLatch mActivityLifecycleLatch = new CountDownLatch(1);
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setUpAndRegisterReceiver();
+ startTestActivity(Utils.LIFECYCLE);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mLifecycleTestBroadcastReceiver != null) {
+ mContext.unregisterReceiver(mLifecycleTestBroadcastReceiver);
+ mLifecycleTestBroadcastReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mLifecycleTestBroadcastReceiver != null) {
+ mContext.unregisterReceiver(mLifecycleTestBroadcastReceiver);
+ }
+ mLifecycleTestBroadcastReceiver = new LifecycleTestReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(action_hasResumed);
+ filter.addAction(action_onPause);
+ filter.addAction(action_onStop);
+ filter.addAction(action_onDestroy);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ mContext.registerReceiver(mLifecycleTestBroadcastReceiver, filter);
+
+ }
+
+ private void waitForOnResume() throws Exception {
+ Log.i(TAG, "waiting for onResume() before continuing");
+ if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+ }
+ }
+
+ private void waitAndSeeIfLifecycleMethodsAreTriggered() throws Exception {
+ if (mActivityLifecycleLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("One or more lifecycle methods were called after triggering assist");
+ }
+ }
+
+ public void testLayerDoesNotTriggerLifecycleMethods() throws Exception {
+ mTestActivity.startTest(Utils.LIFECYCLE);
+ waitForAssistantToBeReady();
+ mTestActivity.start3pApp(Utils.LIFECYCLE);
+ waitForOnResume();
+ startSession();
+ waitForContext();
+ waitAndSeeIfLifecycleMethodsAreTriggered();
+ }
+
+ private class LifecycleTestReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(action_hasResumed)) {
+ mHasResumedLatch.countDown();
+ } else if (action.equals(action_onPause)) {
+ mActivityLifecycleLatch.countDown();
+ } else if (action.equals(action_onStop)) {
+ mActivityLifecycleLatch.countDown();
+ } else if (action.equals(action_onDestroy)) {
+ mActivityLifecycleLatch.countDown();
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+ mAssistantReadyLatch.countDown();
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/testapp/AndroidManifest.xml b/tests/tests/assist/testapp/AndroidManifest.xml
index 371ae7b..8d6169c 100644
--- a/tests/tests/assist/testapp/AndroidManifest.xml
+++ b/tests/tests/assist/testapp/AndroidManifest.xml
@@ -39,5 +39,13 @@
<category android:name="android.intent.category.VOICE" />
</intent-filter>
</activity>
+ <activity android:name="LifecycleActivity"
+ android:label="Life Cycle Check Activity"
+ android:theme="@android:style/Theme.Material.Light">
+ <intent-filter>
+ <action android:name="android.intent.action.TEST_APP_LIFECYCLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
new file mode 100644
index 0000000..4e1dc80
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 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.assist.testapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class LifecycleActivity extends Activity {
+ private static final String TAG = "LifecycleActivity";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "LifecycleActivity created");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.i(TAG, "Activity has resumed");
+ sendBroadcast(new Intent("android.intent.action.lifecycle_hasResumed"));
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.i(TAG, "activity was paused");
+ sendBroadcast(new Intent("android.intent.action.lifecycle_onpause"));
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.i(TAG, "activity was stopped");
+ sendBroadcast(new Intent("android.intent.action.lifecycle_onstop"));
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.i(TAG, "activity was destroyed");
+ sendBroadcast(new Intent("android.intent.action.lifecycle_ondestroy"));
+ }
+}
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
index 83f7549..d9b2ff2 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
@@ -17,6 +17,7 @@
package android.assist.testapp;
import android.app.Activity;
+import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -24,7 +25,6 @@
public class SecureActivity extends Activity {
static final String TAG = "SecureActivity";
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -33,4 +33,11 @@
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE);
}
-}
\ No newline at end of file
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.i(TAG, "Activity has resumed");
+ sendBroadcast(new Intent("android.intent.action.flag_secure_hasResumed"));
+ }
+}
diff --git a/tests/tests/bluetooth/AndroidManifest.xml b/tests/tests/bluetooth/AndroidManifest.xml
index c9ad122..12838f3 100644
--- a/tests/tests/bluetooth/AndroidManifest.xml
+++ b/tests/tests/bluetooth/AndroidManifest.xml
@@ -19,6 +19,7 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<application>
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
index 08bbacd..a79df42 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
@@ -21,28 +21,33 @@
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.ParcelUuid;
import android.os.SystemClock;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
+import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Test cases for Bluetooth LE scans.
* <p>
- * To run the test, the device must be placed in an environment that has at least 5 BLE beacons
- * broadcasting no slower than 1 HZ. The BLE beacons should be a combination of iBeacon devices and
- * non-iBeacon devices.
+ * To run the test, the device must be placed in an environment that has at least 3 beacons, all
+ * placed less than 5 meters away from the DUT.
* <p>
* Run 'run cts --class android.bluetooth.cts.BluetoothLeScanTest' in cts-tradefed to run the test
* cases.
@@ -51,25 +56,26 @@
private static final String TAG = "BluetoothLeScanTest";
- private static final ScanFilter MANUFACTURER_DATA_FILTER =
- new ScanFilter.Builder().setManufacturerData(0x004C, new byte[0], new byte[0]).build();
private static final int SCAN_DURATION_MILLIS = 5000;
- private static final int BATCH_SCAN_REPORT_DELAY_MILLIS = 10000;
+ private static final int BATCH_SCAN_REPORT_DELAY_MILLIS = 20000;
+ private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScanner mScanner;
@Override
public void setUp() {
+ if (!isBleSupported())
+ return;
BluetoothManager manager = (BluetoothManager) mContext.getSystemService(
Context.BLUETOOTH_SERVICE);
- BluetoothAdapter adapter = manager.getAdapter();
- if (!adapter.isEnabled()) {
- adapter.enable();
+ mBluetoothAdapter = manager.getAdapter();
+ if (!mBluetoothAdapter.isEnabled()) {
// Note it's not reliable to listen for Adapter.ACTION_STATE_CHANGED broadcast and check
// bluetooth state.
- sleep(2000);
+ mBluetoothAdapter.enable();
+ sleep(3000);
}
- mScanner = adapter.getBluetoothLeScanner();
+ mScanner = mBluetoothAdapter.getBluetoothLeScanner();
}
/**
@@ -77,13 +83,11 @@
*/
@MediumTest
public void testBasicBleScan() {
- BleScanCallback regularLeScanCallback = new BleScanCallback();
+ if (!isBleSupported())
+ return;
long scanStartMillis = SystemClock.elapsedRealtime();
- mScanner.startScan(regularLeScanCallback);
- sleep(SCAN_DURATION_MILLIS);
- mScanner.stopScan(regularLeScanCallback);
+ Collection<ScanResult> scanResults = scan();
long scanEndMillis = SystemClock.elapsedRealtime();
- Collection<ScanResult> scanResults = regularLeScanCallback.getScanResults();
assertTrue("Scan results shouldn't be empty", !scanResults.isEmpty());
verifyTimestamp(scanResults, scanStartMillis, scanEndMillis);
}
@@ -94,8 +98,16 @@
*/
@MediumTest
public void testScanFilter() {
+ if (!isBleSupported())
+ return;
+
List<ScanFilter> filters = new ArrayList<ScanFilter>();
- filters.add(MANUFACTURER_DATA_FILTER);
+ ScanFilter filter = createScanFilter();
+ if (filter == null) {
+ Log.d(TAG, "no appropriate filter can be set");
+ return;
+ }
+ filters.add(filter);
BleScanCallback filterLeScanCallback = new BleScanCallback();
ScanSettings settings = new ScanSettings.Builder().setScanMode(
@@ -103,22 +115,54 @@
mScanner.startScan(filters, settings, filterLeScanCallback);
sleep(SCAN_DURATION_MILLIS);
mScanner.stopScan(filterLeScanCallback);
+ sleep(1000);
Collection<ScanResult> scanResults = filterLeScanCallback.getScanResults();
- assertTrue("No scan results", !scanResults.isEmpty());
for (ScanResult result : scanResults) {
- assertTrue(MANUFACTURER_DATA_FILTER.matches(result));
+ assertTrue(filter.matches(result));
}
}
+ // Create a scan filter based on the nearby beacon with highest signal strength.
+ private ScanFilter createScanFilter() {
+ // Get a list of nearby beacons.
+ List<ScanResult> scanResults = new ArrayList<ScanResult>(scan());
+ assertTrue("Scan results shouldn't be empty", !scanResults.isEmpty());
+ // Find the beacon with strongest signal strength, which is the target device for filter
+ // scan.
+ Collections.sort(scanResults, new RssiComparator());
+ ScanResult result = scanResults.get(0);
+ ScanRecord record = result.getScanRecord();
+ if (record == null) {
+ return null;
+ }
+ Map<ParcelUuid, byte[]> serviceData = record.getServiceData();
+ if (serviceData != null && !serviceData.isEmpty()) {
+ ParcelUuid uuid = serviceData.keySet().iterator().next();
+ return new ScanFilter.Builder().setServiceData(uuid, new byte[] { 0 },
+ new byte[] { 0 }).build();
+ }
+ SparseArray<byte[]> manufacturerSpecificData = record.getManufacturerSpecificData();
+ if (manufacturerSpecificData != null && manufacturerSpecificData.size() > 0) {
+ return new ScanFilter.Builder().setManufacturerData(manufacturerSpecificData.keyAt(0),
+ new byte[] { 0 }, new byte[] { 0 }).build();
+ }
+ List<ParcelUuid> serviceUuids = record.getServiceUuids();
+ if (serviceUuids != null && !serviceUuids.isEmpty()) {
+ return new ScanFilter.Builder().setServiceUuid(serviceUuids.get(0)).build();
+ }
+ return null;
+ }
+
/**
* Test of opportunistic BLE scans.
*/
@MediumTest
public void testOpportunisticScan() {
- ScanSettings opportunisticScanSettings =
- new ScanSettings.Builder()
- .setScanMode(-1) // TODO: use constants in ScanSettings once it's unhiden.
- .build();
+ if (!isBleSupported())
+ return;
+ ScanSettings opportunisticScanSettings = new ScanSettings.Builder()
+ .setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC)
+ .build();
BleScanCallback emptyScanCallback = new BleScanCallback();
// No scans are really started with opportunistic scans only.
@@ -128,18 +172,20 @@
assertTrue(emptyScanCallback.getScanResults().isEmpty());
BleScanCallback regularScanCallback = new BleScanCallback();
- ScanSettings regularScanSettings =
- new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+ ScanSettings regularScanSettings = new ScanSettings.Builder()
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
List<ScanFilter> filters = new ArrayList<>();
- filters.add(MANUFACTURER_DATA_FILTER);
+ ScanFilter filter = createScanFilter();
+ if (filter != null) {
+ filters.add(filter);
+ } else {
+ Log.d(TAG, "no appropriate filter can be set");
+ }
mScanner.startScan(filters, regularScanSettings, regularScanCallback);
sleep(SCAN_DURATION_MILLIS);
// With normal BLE scan client, opportunistic scan client will get scan results.
assertTrue("opportunistic scan results shouldn't be empty",
!emptyScanCallback.getScanResults().isEmpty());
- assertTrue("opportunistic scan should see more results",
- emptyScanCallback.getScanResults().size() >
- regularScanCallback.getScanResults().size());
// No more scan results for opportunistic scan clients once the normal BLE scan clients
// stops.
@@ -157,20 +203,24 @@
*/
@MediumTest
public void testBatchScan() {
+ if (!isBleSupported() || !isBleBatchScanSupported()) {
+ Log.d(TAG, "BLE or BLE batching not suppported");
+ return;
+ }
ScanSettings batchScanSettings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(BATCH_SCAN_REPORT_DELAY_MILLIS).build();
BleScanCallback batchScanCallback = new BleScanCallback();
- long scanStartMillis = SystemClock.elapsedRealtime();
mScanner.startScan(Collections.<ScanFilter> emptyList(), batchScanSettings,
batchScanCallback);
sleep(SCAN_DURATION_MILLIS);
mScanner.flushPendingScanResults(batchScanCallback);
sleep(1000);
- long scanEndMillis = SystemClock.elapsedRealtime();
List<ScanResult> results = batchScanCallback.getBatchScanResults();
assertTrue(!results.isEmpty());
- verifyTimestamp(results, scanStartMillis, scanEndMillis);
+ long scanEndMillis = SystemClock.elapsedRealtime();
+ mScanner.stopScan(batchScanCallback);
+ verifyTimestamp(results, 0, scanEndMillis);
}
// Verify timestamp of all scan results are within [scanStartMillis, scanEndMillis].
@@ -178,8 +228,10 @@
long scanEndMillis) {
for (ScanResult result : results) {
long timestampMillis = TimeUnit.NANOSECONDS.toMillis(result.getTimestampNanos());
- assertTrue("Invalid timestamp", timestampMillis >= scanStartMillis);
- assertTrue("Invalid timestamp", timestampMillis <= scanEndMillis);
+ assertTrue("Invalid timestamp: " + timestampMillis + " should be >= " + scanStartMillis,
+ timestampMillis >= scanStartMillis);
+ assertTrue("Invalid timestamp: " + timestampMillis + " should be <= " + scanEndMillis,
+ timestampMillis <= scanEndMillis);
}
}
@@ -207,8 +259,8 @@
}
// Return regular BLE scan results accumulated so far.
- synchronized Collection<ScanResult> getScanResults() {
- return Collections.unmodifiableCollection(mResults);
+ synchronized Set<ScanResult> getScanResults() {
+ return Collections.unmodifiableSet(mResults);
}
// Return batch scan results.
@@ -217,6 +269,25 @@
}
}
+ private class RssiComparator implements Comparator<ScanResult> {
+
+ @Override
+ public int compare(ScanResult lhs, ScanResult rhs) {
+ return rhs.getRssi() - lhs.getRssi();
+ }
+
+ }
+
+ // Perform a BLE scan to get results of nearby BLE devices.
+ private Set<ScanResult> scan() {
+ BleScanCallback regularLeScanCallback = new BleScanCallback();
+ mScanner.startScan(regularLeScanCallback);
+ sleep(SCAN_DURATION_MILLIS);
+ mScanner.stopScan(regularLeScanCallback);
+ sleep(1000);
+ return regularLeScanCallback.getScanResults();
+ }
+
// Put the current thread to sleep.
private void sleep(int sleepMillis) {
try {
@@ -226,4 +297,15 @@
}
}
+ // Check if Bluetooth LE feature is supported on DUT.
+ private boolean isBleSupported() {
+ return getContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
+ }
+
+ // Returns whether offloaded scan batching is supported.
+ private boolean isBleBatchScanSupported() {
+ return mBluetoothAdapter.isOffloadedScanBatchingSupported();
+ }
+
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java b/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
index 1881774..8a217fd 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
@@ -32,6 +32,7 @@
private SurfaceView mSurfaceView;
private int currentWidth = 0;
private int currentHeight = 0;
+ private final Object sizeLock = new Object();
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -55,6 +56,12 @@
timeOutMs, expectWidth, expectHeight));
}
+ synchronized(sizeLock) {
+ if (expectWidth == currentWidth && expectHeight == currentHeight) {
+ return true;
+ }
+ }
+
int waitTimeMs = timeOutMs;
boolean changeSucceeded = false;
while (!changeSucceeded && waitTimeMs > 0) {
@@ -87,8 +94,10 @@
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "Surface Changed to: " + width + "x" + height);
- currentWidth = width;
- currentHeight = height;
+ synchronized (sizeLock) {
+ currentWidth = width;
+ currentHeight = height;
+ }
surfaceChangedDone.open();
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
index b752c07..dc499ba 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -748,10 +748,10 @@
resultKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
resultKeys.add(CaptureResult.LENS_POSE_ROTATION);
resultKeys.add(CaptureResult.LENS_POSE_TRANSLATION);
- resultKeys.add(CaptureResult.LENS_INTRINSIC_CALIBRATION);
- resultKeys.add(CaptureResult.LENS_RADIAL_DISTORTION);
resultKeys.add(CaptureResult.LENS_FOCUS_RANGE);
resultKeys.add(CaptureResult.LENS_STATE);
+ resultKeys.add(CaptureResult.LENS_INTRINSIC_CALIBRATION);
+ resultKeys.add(CaptureResult.LENS_RADIAL_DISTORTION);
resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
resultKeys.add(CaptureResult.REQUEST_PIPELINE_DEPTH);
resultKeys.add(CaptureResult.SCALER_CROP_REGION);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 80cd288..6d62067 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -738,6 +738,157 @@
}
/**
+ * Check depth output capability
+ */
+ public void testDepthOutputCharacteristics() {
+ int counter = 0;
+
+ for (CameraCharacteristics c : mCharacteristics) {
+ Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + mIds[counter]);
+
+ int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ assertNotNull("android.request.availableCapabilities must never be null",
+ capabilities);
+ boolean supportDepth = arrayContains(capabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
+ StreamConfigurationMap configs =
+ c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ int[] outputFormats = configs.getOutputFormats();
+ boolean hasDepth16 = arrayContains(outputFormats, ImageFormat.DEPTH16);
+
+ Boolean depthIsExclusive = c.get(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE);
+
+ float[] poseRotation = c.get(CameraCharacteristics.LENS_POSE_ROTATION);
+ float[] poseTranslation = c.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
+ float[] cameraIntrinsics = c.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
+ float[] radialDistortion = c.get(CameraCharacteristics.LENS_RADIAL_DISTORTION);
+
+ if (supportDepth) {
+ mCollector.expectTrue("Supports DEPTH_OUTPUT but does not support DEPTH16",
+ hasDepth16);
+ if (hasDepth16) {
+ Size[] depthSizes = configs.getOutputSizes(ImageFormat.DEPTH16);
+ mCollector.expectTrue("Supports DEPTH_OUTPUT but no sizes for DEPTH16 supported!",
+ depthSizes != null && depthSizes.length > 0);
+ if (depthSizes != null) {
+ for (Size depthSize : depthSizes) {
+ mCollector.expectTrue("All depth16 sizes must be positive",
+ depthSize.getWidth() > 0 && depthSize.getHeight() > 0);
+ long minFrameDuration = configs.getOutputMinFrameDuration(
+ ImageFormat.DEPTH16, depthSize);
+ mCollector.expectTrue("Non-negative min frame duration for depth size "
+ + depthSize + " expected, got " + minFrameDuration,
+ minFrameDuration >= 0);
+ long stallDuration = configs.getOutputStallDuration(
+ ImageFormat.DEPTH16, depthSize);
+ mCollector.expectTrue("Non-negative stall duration for depth size "
+ + depthSize + " expected, got " + stallDuration,
+ stallDuration >= 0);
+ }
+ }
+ }
+ if (arrayContains(outputFormats, ImageFormat.DEPTH_POINT_CLOUD)) {
+ Size[] depthCloudSizes = configs.getOutputSizes(ImageFormat.DEPTH_POINT_CLOUD);
+ mCollector.expectTrue("Supports DEPTH_POINT_CLOUD " +
+ "but no sizes for DEPTH_POINT_CLOUD supported!",
+ depthCloudSizes != null && depthCloudSizes.length > 0);
+ if (depthCloudSizes != null) {
+ for (Size depthCloudSize : depthCloudSizes) {
+ mCollector.expectTrue("All depth point cloud sizes must be nonzero",
+ depthCloudSize.getWidth() > 0);
+ mCollector.expectTrue("All depth point cloud sizes must be N x 1",
+ depthCloudSize.getHeight() == 1);
+ long minFrameDuration = configs.getOutputMinFrameDuration(
+ ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
+ mCollector.expectTrue("Non-negative min frame duration for depth size "
+ + depthCloudSize + " expected, got " + minFrameDuration,
+ minFrameDuration >= 0);
+ long stallDuration = configs.getOutputStallDuration(
+ ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
+ mCollector.expectTrue("Non-negative stall duration for depth size "
+ + depthCloudSize + " expected, got " + stallDuration,
+ stallDuration >= 0);
+ }
+ }
+ }
+
+ mCollector.expectTrue("Supports DEPTH_OUTPUT but DEPTH_IS_EXCLUSIVE is not defined",
+ depthIsExclusive != null);
+
+ mCollector.expectTrue(
+ "Supports DEPTH_OUTPUT but LENS_POSE_ROTATION not right size",
+ poseRotation != null && poseRotation.length == 4);
+ mCollector.expectTrue(
+ "Supports DEPTH_OUTPUT but LENS_POSE_TRANSLATION not right size",
+ poseTranslation != null && poseTranslation.length == 3);
+ mCollector.expectTrue(
+ "Supports DEPTH_OUTPUT but LENS_INTRINSIC_CALIBRATION not right size",
+ cameraIntrinsics != null && cameraIntrinsics.length == 5);
+ mCollector.expectTrue(
+ "Supports DEPTH_OUTPUT but LENS_RADIAL_DISTORTION not right size",
+ radialDistortion != null && radialDistortion.length == 6);
+
+ if (poseRotation != null && poseRotation.length == 4) {
+ float normSq =
+ poseRotation[0] * poseRotation[0] +
+ poseRotation[1] * poseRotation[1] +
+ poseRotation[2] * poseRotation[2] +
+ poseRotation[3] * poseRotation[3];
+ mCollector.expectTrue(
+ "LENS_POSE_ROTATION quarternion must be unit-length",
+ 0.9999f < normSq && normSq < 1.0001f);
+
+ // TODO: Cross-validate orientation/facing and poseRotation
+ Integer orientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
+ Integer facing = c.get(CameraCharacteristics.LENS_FACING);
+ }
+
+ if (poseTranslation != null && poseTranslation.length == 3) {
+ float normSq =
+ poseTranslation[0] * poseTranslation[0] +
+ poseTranslation[1] * poseTranslation[1] +
+ poseTranslation[2] * poseTranslation[2];
+ mCollector.expectTrue("Pose translation is larger than 1 m",
+ normSq < 1.f);
+ }
+
+ Rect precorrectionArray =
+ c.get(CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+ mCollector.expectTrue("Supports DEPTH_OUTPUT but does not have " +
+ "precorrection active array defined", precorrectionArray != null);
+
+ if (cameraIntrinsics != null && precorrectionArray != null) {
+ float fx = cameraIntrinsics[0];
+ float fy = cameraIntrinsics[1];
+ float cx = cameraIntrinsics[2];
+ float cy = cameraIntrinsics[3];
+ float s = cameraIntrinsics[4];
+ mCollector.expectTrue("Optical center expected to be within precorrection array",
+ 0 <= cx && cx < precorrectionArray.width() &&
+ 0 <= cy && cy < precorrectionArray.height());
+
+ // TODO: Verify focal lengths and skew are reasonable
+ }
+
+ if (radialDistortion != null) {
+ // TODO: Verify radial distortion
+ }
+
+ } else {
+ boolean hasFields =
+ hasDepth16 && (poseTranslation != null) &&
+ (poseRotation != null) && (cameraIntrinsics != null) &&
+ (radialDistortion != null) && (depthIsExclusive != null);
+
+ mCollector.expectTrue(
+ "All necessary depth fields defined, but DEPTH_OUTPUT capability is not listed",
+ !hasFields);
+ }
+ }
+ }
+
+ /**
* Cross-check StreamConfigurationMap output
*/
public void testStreamConfigurationMap() throws Exception {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
index 4415ecc..3e3e81e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -397,6 +397,9 @@
case REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING:
// Tested in ExtendedCameraCharacteristicsTest
return;
+ case REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT:
+ // Tested in ExtendedCameracharacteristicsTest
+ return;
default:
capabilityName = "Unknown";
assertTrue(String.format("Unknown capability set: %d", capability),
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index 7330a4c..c136b67 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -619,8 +619,8 @@
mPreviewSize.getHeight());
assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
mPreviewSurface = holder.getSurface();
- assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
assertNotNull("Preview surface is null", mPreviewSurface);
+ assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
}
/**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
index e5a5053..d246ec5 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
@@ -44,7 +44,8 @@
// sensorType: threshold (% of expected period)
private static final SparseIntArray DEFAULTS = new SparseIntArray(12);
// Max allowed jitter (in percentage).
- private static final int THRESHOLD_PERCENT_FOR_HIFI_SENSORS = 1;
+ private static final int GRACE_FACTOR = 2;
+ private static final int THRESHOLD_PERCENT_FOR_HIFI_SENSORS = 1 * GRACE_FACTOR;
static {
// Use a method so that the @deprecation warning can be set for that method only
setDefaults();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
index 20dd2d2..1e1c950 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
@@ -66,23 +66,31 @@
*/
public static StandardDeviationVerification getDefault(TestSensorEnvironment environment) {
int sensorType = environment.getSensor().getType();
+ float mGraceFactorAccelGyro = 2.0f;
+ float mGraceFactorMagPressure = 4.0f;
+ float mMaxBandWidth = (float) environment.getFrequencyHz();
+ float mAccelNoise = (float)(mGraceFactorAccelGyro * Math.sqrt(mMaxBandWidth) * (9.81 * 0.0004));
+ float mGyroNoise = (float)(mGraceFactorAccelGyro * Math.sqrt(mMaxBandWidth) * (Math.PI/180.0 * 0.014));
+ float mMagNoise = (float)((mGraceFactorMagPressure) * 0.5); // Allow extra grace for mag
+ float mPressureNoise = (float)(mGraceFactorMagPressure * 0.02 * (float)Math.sqrt(mMaxBandWidth)); // Allow extra grace for pressure
+
if (!DEFAULTS.containsKey(sensorType)) {
return null;
}
+
boolean hasHifiSensors = environment.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_HIFI_SENSORS);
if (hasHifiSensors) {
- // Max accelerometer deviation: 400uG/√Hz
- DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new float[]{0.004f, 0.004f, 0.004f});
+
+ DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new float[]{mAccelNoise, mAccelNoise, mAccelNoise});
// Max gyro deviation: 0.014°/s/√Hz
- float deviationInRadians = (float) (0.014f * (Math.PI / 180));
DEFAULTS.put(Sensor.TYPE_GYROSCOPE,
- new float[]{deviationInRadians,deviationInRadians, deviationInRadians});
+ new float[]{mGyroNoise, mGyroNoise, mGyroNoise});
// Max magnetometer deviation: 0.1uT/√Hz
- DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD, new float[]{0.1f, 0.1f, 0.1f});
+ DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD, new float[]{mMagNoise, mMagNoise, mMagNoise});
// Max pressure deviation: 2Pa/√Hz
- DEFAULTS.put(Sensor.TYPE_PRESSURE, new float[]{2.0f, 2.0f, 2.0f});
+ DEFAULTS.put(Sensor.TYPE_PRESSURE, new float[]{mPressureNoise,mPressureNoise,mPressureNoise});
}
return new StandardDeviationVerification(DEFAULTS.get(sensorType));
}
diff --git a/tests/tests/os/src/android/os/cts/StatFsTest.java b/tests/tests/os/src/android/os/cts/StatFsTest.java
index 67afde9..a0653fd 100644
--- a/tests/tests/os/src/android/os/cts/StatFsTest.java
+++ b/tests/tests/os/src/android/os/cts/StatFsTest.java
@@ -46,15 +46,15 @@
assertTrue(stat.getBlockSize() > 0);
assertTrue(stat.getBlockCount() > 0);
assertTrue(stat.getFreeBlocks() >= stat.getAvailableBlocks());
- assertTrue(stat.getAvailableBlocks() > 0);
+ assertTrue(stat.getAvailableBlocks() >= 0);
assertTrue(stat.getBlockSizeLong() > 0);
assertTrue(stat.getBlockCountLong() > 0);
assertTrue(stat.getFreeBlocksLong() >= stat.getAvailableBlocksLong());
- assertTrue(stat.getAvailableBlocksLong() > 0);
+ assertTrue(stat.getAvailableBlocksLong() >= 0);
- assertTrue(stat.getFreeBytes() > 0);
- assertTrue(stat.getAvailableBytes() > 0);
+ assertTrue(stat.getFreeBytes() >= 0);
+ assertTrue(stat.getAvailableBytes() >= 0);
assertTrue(stat.getTotalBytes() > 0);
}
}
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 7b4adb0..cf6a09d 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -371,11 +371,11 @@
if (!arch.contains("arm") ||
Integer.parseInt(osVersion[0]) < 2 ||
(Integer.parseInt(osVersion[0]) == 3 &&
- Integer.parseInt(osVersion[1]) < 10) ||
- flavor.contains("sprout") || flavor.contains("sprout_b"))
+ Integer.parseInt(osVersion[1]) < 10))
return;
final File f = new File("/proc/device-tree/cpus");
- assertTrue(f.exists());
+ if (!f.exists())
+ return;
String[] dir = f.list(new FilenameFilter() {
@Override
public boolean accept(File pathname, String name) {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
index fa3a70d..8fc5df1 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
@@ -379,7 +379,7 @@
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mp3");
Uri fileUri = mResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
// give media provider some time to realize there's no album art
- //SystemClock.sleep(1000);
+ SystemClock.sleep(1000);
// get its album id
Cursor c = mResolver.query(fileUri, new String[] { MediaStore.Audio.Media.ALBUM_ID},
null, null, null);
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMax.java b/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMax.java
index f646f07..961dafa 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMax.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMax.java
@@ -358,6 +358,204 @@
(relaxed ? "_relaxed" : "") + ":\n" + message.toString(), errorFound);
}
+ private void checkMaxFloat2FloatFloat2() {
+ Allocation inA = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x8592438l, false);
+ Allocation inB = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x8592439l, false);
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+ script.set_gAllocInB(inB);
+ script.forEach_testMaxFloat2FloatFloat2(inA, out);
+ verifyResultsMaxFloat2FloatFloat2(inA, inB, out, false);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxFloat2FloatFloat2: " + e.toString());
+ }
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+ scriptRelaxed.set_gAllocInB(inB);
+ scriptRelaxed.forEach_testMaxFloat2FloatFloat2(inA, out);
+ verifyResultsMaxFloat2FloatFloat2(inA, inB, out, true);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxFloat2FloatFloat2: " + e.toString());
+ }
+ }
+
+ private void verifyResultsMaxFloat2FloatFloat2(Allocation inA, Allocation inB, Allocation out, boolean relaxed) {
+ float[] arrayInA = new float[INPUTSIZE * 2];
+ inA.copyTo(arrayInA);
+ float[] arrayInB = new float[INPUTSIZE * 1];
+ inB.copyTo(arrayInB);
+ float[] arrayOut = new float[INPUTSIZE * 2];
+ out.copyTo(arrayOut);
+ for (int i = 0; i < INPUTSIZE; i++) {
+ for (int j = 0; j < 2 ; j++) {
+ // Extract the inputs.
+ ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+ args.inA = arrayInA[i * 2 + j];
+ args.inB = arrayInB[i];
+ // Figure out what the outputs should have been.
+ Target target = new Target(relaxed);
+ CoreMathVerifier.computeMax(args, target);
+ // Validate the outputs.
+ boolean valid = true;
+ if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+ valid = false;
+ }
+ if (!valid) {
+ StringBuilder message = new StringBuilder();
+ message.append("Input inA: ");
+ appendVariableToMessage(message, args.inA);
+ message.append("\n");
+ message.append("Input inB: ");
+ appendVariableToMessage(message, args.inB);
+ message.append("\n");
+ message.append("Expected output out: ");
+ appendVariableToMessage(message, args.out);
+ message.append("\n");
+ message.append("Actual output out: ");
+ appendVariableToMessage(message, arrayOut[i * 2 + j]);
+ if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+ message.append(" FAIL");
+ }
+ message.append("\n");
+ assertTrue("Incorrect output for checkMaxFloat2FloatFloat2" +
+ (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+ }
+ }
+ }
+ }
+
+ private void checkMaxFloat3FloatFloat3() {
+ Allocation inA = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xf6c41894l, false);
+ Allocation inB = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xf6c41895l, false);
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+ script.set_gAllocInB(inB);
+ script.forEach_testMaxFloat3FloatFloat3(inA, out);
+ verifyResultsMaxFloat3FloatFloat3(inA, inB, out, false);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxFloat3FloatFloat3: " + e.toString());
+ }
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+ scriptRelaxed.set_gAllocInB(inB);
+ scriptRelaxed.forEach_testMaxFloat3FloatFloat3(inA, out);
+ verifyResultsMaxFloat3FloatFloat3(inA, inB, out, true);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxFloat3FloatFloat3: " + e.toString());
+ }
+ }
+
+ private void verifyResultsMaxFloat3FloatFloat3(Allocation inA, Allocation inB, Allocation out, boolean relaxed) {
+ float[] arrayInA = new float[INPUTSIZE * 4];
+ inA.copyTo(arrayInA);
+ float[] arrayInB = new float[INPUTSIZE * 1];
+ inB.copyTo(arrayInB);
+ float[] arrayOut = new float[INPUTSIZE * 4];
+ out.copyTo(arrayOut);
+ for (int i = 0; i < INPUTSIZE; i++) {
+ for (int j = 0; j < 3 ; j++) {
+ // Extract the inputs.
+ ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+ args.inA = arrayInA[i * 4 + j];
+ args.inB = arrayInB[i];
+ // Figure out what the outputs should have been.
+ Target target = new Target(relaxed);
+ CoreMathVerifier.computeMax(args, target);
+ // Validate the outputs.
+ boolean valid = true;
+ if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+ valid = false;
+ }
+ if (!valid) {
+ StringBuilder message = new StringBuilder();
+ message.append("Input inA: ");
+ appendVariableToMessage(message, args.inA);
+ message.append("\n");
+ message.append("Input inB: ");
+ appendVariableToMessage(message, args.inB);
+ message.append("\n");
+ message.append("Expected output out: ");
+ appendVariableToMessage(message, args.out);
+ message.append("\n");
+ message.append("Actual output out: ");
+ appendVariableToMessage(message, arrayOut[i * 4 + j]);
+ if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+ message.append(" FAIL");
+ }
+ message.append("\n");
+ assertTrue("Incorrect output for checkMaxFloat3FloatFloat3" +
+ (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+ }
+ }
+ }
+ }
+
+ private void checkMaxFloat4FloatFloat4() {
+ Allocation inA = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xe52f0cf0l, false);
+ Allocation inB = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xe52f0cf1l, false);
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+ script.set_gAllocInB(inB);
+ script.forEach_testMaxFloat4FloatFloat4(inA, out);
+ verifyResultsMaxFloat4FloatFloat4(inA, inB, out, false);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxFloat4FloatFloat4: " + e.toString());
+ }
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+ scriptRelaxed.set_gAllocInB(inB);
+ scriptRelaxed.forEach_testMaxFloat4FloatFloat4(inA, out);
+ verifyResultsMaxFloat4FloatFloat4(inA, inB, out, true);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxFloat4FloatFloat4: " + e.toString());
+ }
+ }
+
+ private void verifyResultsMaxFloat4FloatFloat4(Allocation inA, Allocation inB, Allocation out, boolean relaxed) {
+ float[] arrayInA = new float[INPUTSIZE * 4];
+ inA.copyTo(arrayInA);
+ float[] arrayInB = new float[INPUTSIZE * 1];
+ inB.copyTo(arrayInB);
+ float[] arrayOut = new float[INPUTSIZE * 4];
+ out.copyTo(arrayOut);
+ for (int i = 0; i < INPUTSIZE; i++) {
+ for (int j = 0; j < 4 ; j++) {
+ // Extract the inputs.
+ ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+ args.inA = arrayInA[i * 4 + j];
+ args.inB = arrayInB[i];
+ // Figure out what the outputs should have been.
+ Target target = new Target(relaxed);
+ CoreMathVerifier.computeMax(args, target);
+ // Validate the outputs.
+ boolean valid = true;
+ if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+ valid = false;
+ }
+ if (!valid) {
+ StringBuilder message = new StringBuilder();
+ message.append("Input inA: ");
+ appendVariableToMessage(message, args.inA);
+ message.append("\n");
+ message.append("Input inB: ");
+ appendVariableToMessage(message, args.inB);
+ message.append("\n");
+ message.append("Expected output out: ");
+ appendVariableToMessage(message, args.out);
+ message.append("\n");
+ message.append("Actual output out: ");
+ appendVariableToMessage(message, arrayOut[i * 4 + j]);
+ if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+ message.append(" FAIL");
+ }
+ message.append("\n");
+ assertTrue("Incorrect output for checkMaxFloat4FloatFloat4" +
+ (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+ }
+ }
+ }
+ }
+
public class ArgumentsCharCharChar {
public byte inA;
public byte inB;
@@ -2907,6 +3105,9 @@
checkMaxFloat2Float2Float2();
checkMaxFloat3Float3Float3();
checkMaxFloat4Float4Float4();
+ checkMaxFloat2FloatFloat2();
+ checkMaxFloat3FloatFloat3();
+ checkMaxFloat4FloatFloat4();
checkMaxCharCharChar();
checkMaxChar2Char2Char2();
checkMaxChar3Char3Char3();
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMax.rs b/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMax.rs
index b582b0d..a9ca895 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMax.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMax.rs
@@ -41,6 +41,21 @@
return max(inA, inB);
}
+float2 __attribute__((kernel)) testMaxFloat2FloatFloat2(float2 inA, unsigned int x) {
+ float inB = rsGetElementAt_float(gAllocInB, x);
+ return max(inA, inB);
+}
+
+float3 __attribute__((kernel)) testMaxFloat3FloatFloat3(float3 inA, unsigned int x) {
+ float inB = rsGetElementAt_float(gAllocInB, x);
+ return max(inA, inB);
+}
+
+float4 __attribute__((kernel)) testMaxFloat4FloatFloat4(float4 inA, unsigned int x) {
+ float inB = rsGetElementAt_float(gAllocInB, x);
+ return max(inA, inB);
+}
+
char __attribute__((kernel)) testMaxCharCharChar(char inA, unsigned int x) {
char inB = rsGetElementAt_char(gAllocInB, x);
return max(inA, inB);
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMin.java b/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMin.java
index 9151fd1..f71fac8 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMin.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMin.java
@@ -358,6 +358,204 @@
(relaxed ? "_relaxed" : "") + ":\n" + message.toString(), errorFound);
}
+ private void checkMinFloat2FloatFloat2() {
+ Allocation inA = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x503b89a6l, false);
+ Allocation inB = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x503b89a7l, false);
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+ script.set_gAllocInB(inB);
+ script.forEach_testMinFloat2FloatFloat2(inA, out);
+ verifyResultsMinFloat2FloatFloat2(inA, inB, out, false);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinFloat2FloatFloat2: " + e.toString());
+ }
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+ scriptRelaxed.set_gAllocInB(inB);
+ scriptRelaxed.forEach_testMinFloat2FloatFloat2(inA, out);
+ verifyResultsMinFloat2FloatFloat2(inA, inB, out, true);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinFloat2FloatFloat2: " + e.toString());
+ }
+ }
+
+ private void verifyResultsMinFloat2FloatFloat2(Allocation inA, Allocation inB, Allocation out, boolean relaxed) {
+ float[] arrayInA = new float[INPUTSIZE * 2];
+ inA.copyTo(arrayInA);
+ float[] arrayInB = new float[INPUTSIZE * 1];
+ inB.copyTo(arrayInB);
+ float[] arrayOut = new float[INPUTSIZE * 2];
+ out.copyTo(arrayOut);
+ for (int i = 0; i < INPUTSIZE; i++) {
+ for (int j = 0; j < 2 ; j++) {
+ // Extract the inputs.
+ ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+ args.inA = arrayInA[i * 2 + j];
+ args.inB = arrayInB[i];
+ // Figure out what the outputs should have been.
+ Target target = new Target(relaxed);
+ CoreMathVerifier.computeMin(args, target);
+ // Validate the outputs.
+ boolean valid = true;
+ if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+ valid = false;
+ }
+ if (!valid) {
+ StringBuilder message = new StringBuilder();
+ message.append("Input inA: ");
+ appendVariableToMessage(message, args.inA);
+ message.append("\n");
+ message.append("Input inB: ");
+ appendVariableToMessage(message, args.inB);
+ message.append("\n");
+ message.append("Expected output out: ");
+ appendVariableToMessage(message, args.out);
+ message.append("\n");
+ message.append("Actual output out: ");
+ appendVariableToMessage(message, arrayOut[i * 2 + j]);
+ if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+ message.append(" FAIL");
+ }
+ message.append("\n");
+ assertTrue("Incorrect output for checkMinFloat2FloatFloat2" +
+ (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+ }
+ }
+ }
+ }
+
+ private void checkMinFloat3FloatFloat3() {
+ Allocation inA = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x3ea67e02l, false);
+ Allocation inB = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x3ea67e03l, false);
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+ script.set_gAllocInB(inB);
+ script.forEach_testMinFloat3FloatFloat3(inA, out);
+ verifyResultsMinFloat3FloatFloat3(inA, inB, out, false);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinFloat3FloatFloat3: " + e.toString());
+ }
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+ scriptRelaxed.set_gAllocInB(inB);
+ scriptRelaxed.forEach_testMinFloat3FloatFloat3(inA, out);
+ verifyResultsMinFloat3FloatFloat3(inA, inB, out, true);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinFloat3FloatFloat3: " + e.toString());
+ }
+ }
+
+ private void verifyResultsMinFloat3FloatFloat3(Allocation inA, Allocation inB, Allocation out, boolean relaxed) {
+ float[] arrayInA = new float[INPUTSIZE * 4];
+ inA.copyTo(arrayInA);
+ float[] arrayInB = new float[INPUTSIZE * 1];
+ inB.copyTo(arrayInB);
+ float[] arrayOut = new float[INPUTSIZE * 4];
+ out.copyTo(arrayOut);
+ for (int i = 0; i < INPUTSIZE; i++) {
+ for (int j = 0; j < 3 ; j++) {
+ // Extract the inputs.
+ ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+ args.inA = arrayInA[i * 4 + j];
+ args.inB = arrayInB[i];
+ // Figure out what the outputs should have been.
+ Target target = new Target(relaxed);
+ CoreMathVerifier.computeMin(args, target);
+ // Validate the outputs.
+ boolean valid = true;
+ if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+ valid = false;
+ }
+ if (!valid) {
+ StringBuilder message = new StringBuilder();
+ message.append("Input inA: ");
+ appendVariableToMessage(message, args.inA);
+ message.append("\n");
+ message.append("Input inB: ");
+ appendVariableToMessage(message, args.inB);
+ message.append("\n");
+ message.append("Expected output out: ");
+ appendVariableToMessage(message, args.out);
+ message.append("\n");
+ message.append("Actual output out: ");
+ appendVariableToMessage(message, arrayOut[i * 4 + j]);
+ if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+ message.append(" FAIL");
+ }
+ message.append("\n");
+ assertTrue("Incorrect output for checkMinFloat3FloatFloat3" +
+ (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+ }
+ }
+ }
+ }
+
+ private void checkMinFloat4FloatFloat4() {
+ Allocation inA = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x2d11725el, false);
+ Allocation inB = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x2d11725fl, false);
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+ script.set_gAllocInB(inB);
+ script.forEach_testMinFloat4FloatFloat4(inA, out);
+ verifyResultsMinFloat4FloatFloat4(inA, inB, out, false);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinFloat4FloatFloat4: " + e.toString());
+ }
+ try {
+ Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+ scriptRelaxed.set_gAllocInB(inB);
+ scriptRelaxed.forEach_testMinFloat4FloatFloat4(inA, out);
+ verifyResultsMinFloat4FloatFloat4(inA, inB, out, true);
+ } catch (Exception e) {
+ throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinFloat4FloatFloat4: " + e.toString());
+ }
+ }
+
+ private void verifyResultsMinFloat4FloatFloat4(Allocation inA, Allocation inB, Allocation out, boolean relaxed) {
+ float[] arrayInA = new float[INPUTSIZE * 4];
+ inA.copyTo(arrayInA);
+ float[] arrayInB = new float[INPUTSIZE * 1];
+ inB.copyTo(arrayInB);
+ float[] arrayOut = new float[INPUTSIZE * 4];
+ out.copyTo(arrayOut);
+ for (int i = 0; i < INPUTSIZE; i++) {
+ for (int j = 0; j < 4 ; j++) {
+ // Extract the inputs.
+ ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+ args.inA = arrayInA[i * 4 + j];
+ args.inB = arrayInB[i];
+ // Figure out what the outputs should have been.
+ Target target = new Target(relaxed);
+ CoreMathVerifier.computeMin(args, target);
+ // Validate the outputs.
+ boolean valid = true;
+ if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+ valid = false;
+ }
+ if (!valid) {
+ StringBuilder message = new StringBuilder();
+ message.append("Input inA: ");
+ appendVariableToMessage(message, args.inA);
+ message.append("\n");
+ message.append("Input inB: ");
+ appendVariableToMessage(message, args.inB);
+ message.append("\n");
+ message.append("Expected output out: ");
+ appendVariableToMessage(message, args.out);
+ message.append("\n");
+ message.append("Actual output out: ");
+ appendVariableToMessage(message, arrayOut[i * 4 + j]);
+ if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+ message.append(" FAIL");
+ }
+ message.append("\n");
+ assertTrue("Incorrect output for checkMinFloat4FloatFloat4" +
+ (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+ }
+ }
+ }
+ }
+
public class ArgumentsCharCharChar {
public byte inA;
public byte inB;
@@ -2907,6 +3105,9 @@
checkMinFloat2Float2Float2();
checkMinFloat3Float3Float3();
checkMinFloat4Float4Float4();
+ checkMinFloat2FloatFloat2();
+ checkMinFloat3FloatFloat3();
+ checkMinFloat4FloatFloat4();
checkMinCharCharChar();
checkMinChar2Char2Char2();
checkMinChar3Char3Char3();
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMin.rs b/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMin.rs
index eec9fb6..dccff70 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMin.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/generated/TestMin.rs
@@ -41,6 +41,21 @@
return min(inA, inB);
}
+float2 __attribute__((kernel)) testMinFloat2FloatFloat2(float2 inA, unsigned int x) {
+ float inB = rsGetElementAt_float(gAllocInB, x);
+ return min(inA, inB);
+}
+
+float3 __attribute__((kernel)) testMinFloat3FloatFloat3(float3 inA, unsigned int x) {
+ float inB = rsGetElementAt_float(gAllocInB, x);
+ return min(inA, inB);
+}
+
+float4 __attribute__((kernel)) testMinFloat4FloatFloat4(float4 inA, unsigned int x) {
+ float inB = rsGetElementAt_float(gAllocInB, x);
+ return min(inA, inB);
+}
+
char __attribute__((kernel)) testMinCharCharChar(char inA, unsigned int x) {
char inB = rsGetElementAt_char(gAllocInB, x);
return min(inA, inB);
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index 6bef886..2d55fb6 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -30,10 +30,14 @@
android_security_cts_NativeCodeTest.cpp \
android_security_cts_SELinuxTest.cpp \
android_security_cts_MMapExecutableTest.cpp \
- android_security_cts_AudioPolicyBinderTest.cpp
+ android_security_cts_AudioPolicyBinderTest.cpp \
+ android_security_cts_EncryptionTest.cpp
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_SHARED_LIBRARIES := libnativehelper liblog libbinder libutils libmedia libselinux libdl
+LOCAL_SHARED_LIBRARIES := libnativehelper liblog libbinder libutils libmedia libselinux libdl libcutils libcrypto
+
+LOCAL_C_INCLUDES += ndk/sources/cpufeatures
+LOCAL_STATIC_LIBRARIES := cpufeatures
include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
index 1051a82..c60b866 100644
--- a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
+++ b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
@@ -25,6 +25,7 @@
extern int register_android_security_cts_SELinuxTest(JNIEnv*);
extern int register_android_security_cts_MMapExecutableTest(JNIEnv* env);
extern int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env);
+extern int register_android_security_cts_EncryptionTest(JNIEnv* env);
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
@@ -65,5 +66,9 @@
return JNI_ERR;
}
+ if (register_android_security_cts_EncryptionTest(env)) {
+ return JNI_ERR;
+ }
+
return JNI_VERSION_1_4;
}
diff --git a/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp b/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp
new file mode 100644
index 0000000..b9e390e
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_EncryptionTest.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <cpu-features.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <jni.h>
+#include <JNIHelp.h>
+#include <openssl/aes.h>
+#include <openssl/cpu.h>
+#include <openssl/evp.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <new>
+
+#define TEST_EVP_CIPHER EVP_aes_256_cbc()
+#define TEST_BUFSIZE (1 * 1024 * 1024) /* 1 MiB */
+#define TEST_ITERATIONS 100 /* MiB */
+#define TEST_THRESHOLD 2000 /* ms */
+
+/*
+ * Function: deviceIsEncrypted
+ * Purpose: Check the device is encrypted
+ * Parameters: none
+ * Returns: boolean: (true) if encrypted, (false) otherwise
+ * Exceptions: none
+ */
+static jboolean android_security_cts_EncryptionTest_deviceIsEncrypted(JNIEnv *, jobject)
+{
+ char prop_value[PROP_VALUE_MAX];
+ property_get("ro.crypto.state", prop_value, "");
+
+ jboolean rc = !strcmp(prop_value, "encrypted");
+ ALOGE("EncryptionTest::deviceIsEncrypted: %d", rc);
+
+ return rc;
+}
+
+/*
+ * Function: cpuHasAes
+ * Purpose: Check if we have an ARM CPU with AES instruction
+ * Parameters: none
+ * Returns: boolean: (true) if AES is available, (false) otherwise
+ * Exceptions: none
+ */
+static jboolean android_security_cts_EncryptionTest_cpuHasAes(JNIEnv *, jobject)
+{
+ jboolean rc = false;
+ AndroidCpuFamily family = android_getCpuFamily();
+ uint64_t features = android_getCpuFeatures();
+
+ if (family == ANDROID_CPU_FAMILY_ARM) {
+ rc = (features & ANDROID_CPU_ARM_FEATURE_AES) != 0;
+ } else if (family == ANDROID_CPU_FAMILY_ARM64) {
+ rc = (features & ANDROID_CPU_ARM64_FEATURE_AES) != 0;
+ }
+
+ ALOGE("EncryptionTest::cpuHasAes: %d", rc);
+ return rc;
+}
+
+/*
+ * Function: cpuHasNeon
+ * Purpose: Check if we have an ARM CPU with NEON instructions
+ * Parameters: none
+ * Returns: boolean: (true) if NEON is available, (false) otherwise
+ * Exceptions: none
+ */
+static jboolean android_security_cts_EncryptionTest_cpuHasNeon(JNIEnv *, jobject)
+{
+ jboolean rc = false;
+ AndroidCpuFamily family = android_getCpuFamily();
+
+ if (family == ANDROID_CPU_FAMILY_ARM) {
+ rc = (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
+ } else {
+ rc = (family == ANDROID_CPU_FAMILY_ARM64);
+ }
+
+ ALOGE("EncryptionTest::cpuHasNeon: %d", rc);
+ return rc;
+}
+
+/*
+ * Function: neonIsEnabled
+ * Purpose: Check if libcrypto is compiled with NEON support
+ * Parameters: none
+ * Returns: boolean: (true) if NEON is available, (false) otherwise
+ * Exceptions: none
+ */
+static jboolean android_security_cts_EncryptionTest_neonIsEnabled(JNIEnv *, jobject)
+{
+#if defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64)
+ jboolean rc = CRYPTO_is_NEON_capable();
+#else
+ jboolean rc = false;
+#endif
+
+ ALOGE("EncryptionTest::neonIsEnabled: %d", rc);
+ return rc;
+}
+
+static inline uint64_t ns()
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec;
+}
+
+/*
+ * Function: aesIsFast
+ * Purpose: Test if AES performance is sufficient to require encryption
+ * Parameters: none
+ * Returns: boolean: (true) if AES performance is acceptable, (false) otherwise
+ * Exceptions: InvalidKeyException if EVP_DecryptInit fails, OutOfMemoryError
+ * if memory allocation fails.
+ */
+static jboolean android_security_cts_EncryptionTest_aesIsFast(JNIEnv *env, jobject)
+{
+ EVP_CIPHER_CTX ctx;
+ uint8_t *buf;
+ uint8_t key[EVP_CIPHER_key_length(TEST_EVP_CIPHER)];
+ uint8_t iv[EVP_CIPHER_iv_length(TEST_EVP_CIPHER)];
+
+ memset(key, 0x42, sizeof(key));
+ memset(iv, 0x11, sizeof(iv));
+
+ EVP_CIPHER_CTX_init(&ctx);
+
+ if (!EVP_DecryptInit(&ctx, TEST_EVP_CIPHER, key, iv)) {
+ jniThrowException(env, "java/security/InvalidKeyException",
+ "EVP_DecryptInit failed");
+ return false;
+ }
+
+ buf = new (std::nothrow) uint8_t[TEST_BUFSIZE +
+ EVP_CIPHER_block_size(TEST_EVP_CIPHER)];
+
+ if (!buf) {
+ jniThrowException(env, "java/lang/OutOfMemoryError",
+ "Failed to allocate test buffer");
+ return false;
+ }
+
+ memset(buf, 0xF0, TEST_BUFSIZE);
+
+ int len;
+ uint64_t t = ns();
+
+ for (int i = 0; i < TEST_ITERATIONS; ++i) {
+ EVP_DecryptUpdate(&ctx, buf, &len, buf, TEST_BUFSIZE);
+ }
+
+ t = ns() - t;
+
+ delete[] buf;
+
+ unsigned long ms = (unsigned long)(t / 1000000);
+ double speed =
+ (double)(TEST_ITERATIONS * TEST_BUFSIZE / (1024 * 1024)) * 1000.0 / ms;
+
+ ALOGE("EncryptionTest::aesIsFast: %u iterations in %lu ms (%.01lf MiB/s) "
+ "(threshold %u ms)", TEST_ITERATIONS, ms, speed, TEST_THRESHOLD);
+
+ return ms < TEST_THRESHOLD;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "deviceIsEncrypted", "()Z",
+ (void *) android_security_cts_EncryptionTest_deviceIsEncrypted },
+ { "cpuHasAes", "()Z",
+ (void *) android_security_cts_EncryptionTest_cpuHasAes },
+ { "cpuHasNeon", "()Z",
+ (void *) android_security_cts_EncryptionTest_cpuHasNeon },
+ { "neonIsEnabled", "()Z",
+ (void *) android_security_cts_EncryptionTest_neonIsEnabled },
+ { "aesIsFast", "()Z",
+ (void *) android_security_cts_EncryptionTest_aesIsFast }
+};
+
+int register_android_security_cts_EncryptionTest(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/security/cts/EncryptionTest");
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
index 0ef08f0..d188aab 100644
--- a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
+++ b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
@@ -123,6 +123,7 @@
*/
int firstPid = -1;
int previousPid = -1;
+ int lastPid = -1;
for (int i = 0; i < MAX_PID; i++) {
byte[] output = new byte[RANDOM_BYTES_PER_PID];
int pid;
@@ -205,7 +206,9 @@
firstPid = pid;
}
- if (i > PRIMING_ITERATIONS) {
+ if (i <= PRIMING_ITERATIONS) {
+ lastPid = pid;
+ } else if (pid > lastPid && (lastPid > firstPid || pid < firstPid)) {
wastePids(firstPid, previousPid);
}
}
diff --git a/tests/tests/security/src/android/security/cts/EncryptionTest.java b/tests/tests/security/src/android/security/cts/EncryptionTest.java
new file mode 100644
index 0000000..bd9a458
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/EncryptionTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 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.security.cts;
+
+import android.test.AndroidTestCase;
+import junit.framework.TestCase;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.util.Log;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class EncryptionTest extends AndroidTestCase {
+
+ static {
+ System.loadLibrary("ctssecurity_jni");
+ }
+
+ private static final String TAG = "EncryptionTest";
+
+ private static final String crypto = "/proc/crypto";
+
+ private static native boolean deviceIsEncrypted();
+
+ private static native boolean cpuHasAes();
+
+ private static native boolean cpuHasNeon();
+
+ private static native boolean neonIsEnabled();
+
+ private static native boolean aesIsFast();
+
+ private boolean hasKernelCrypto(String driver) throws Exception {
+ BufferedReader br = new BufferedReader(new FileReader(crypto));
+ Pattern p = Pattern.compile("driver\\s*:\\s*" + driver);
+
+ try {
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (p.matcher(line).matches()) {
+ Log.i(TAG, crypto + " has " + driver + " (" + line + ")");
+ return true;
+ }
+ }
+ } finally {
+ br.close();
+ }
+
+ return false;
+ }
+
+ private boolean hasLowRAM() {
+ ActivityManager activityManager =
+ (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
+
+ return activityManager.isLowRamDevice();
+ }
+
+ public void testConfig() throws Exception {
+ if (cpuHasAes()) {
+ // If CPU has AES CE, it must be enabled in kernel
+ assertTrue(crypto + " is missing xts-aes-ce",
+ hasKernelCrypto("xts-aes-ce"));
+ } else if (cpuHasNeon()) {
+ // Otherwise, if CPU has NEON, it must be enabled
+ assertTrue(crypto + " is missing xts-aes-neon (or xts-aes-neonbs)",
+ hasKernelCrypto("xts-aes-neon") ||
+ hasKernelCrypto("xts-aes-neonbs") ||
+ hasKernelCrypto("aes-asm")); // Not recommended alone
+ }
+
+ if (cpuHasNeon()) {
+ assertTrue("libcrypto must have NEON", neonIsEnabled());
+ }
+ }
+
+ public void testEncryption() throws Exception {
+ if (deviceIsEncrypted()) {
+ return;
+ }
+
+ // Optional for low RAM devices
+ if (hasLowRAM()) {
+ Log.i(TAG, "hasLowRAM: true");
+ return;
+ }
+
+ // Required if performance is sufficient
+ assertFalse("Device encryption is required", aesIsFast());
+ }
+}
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
index 8fb61bf..4ba1160d 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
@@ -50,6 +50,8 @@
public static final String RESOLUTION = "resolution";
public static final String VERSION_SDK = "androidPlatformVersion";
public static final String VERSION_RELEASE = "buildVersion";
+ public static final String VERSION_BASE_OS = "base_os";
+ public static final String VERSION_SECURITY_PATCH = "security_patch";
public static final String BUILD_ABI = "build_abi";
public static final String BUILD_ABI2 = "build_abi2";
public static final String BUILD_ABIS = "build_abis";
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
index 5828259..51da850 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
@@ -80,6 +80,8 @@
addResult(VERSION_RELEASE, Build.VERSION.RELEASE);
addResult(VERSION_SDK, Build.VERSION.SDK);
+ addResult(VERSION_BASE_OS, Build.VERSION.BASE_OS);
+ addResult(VERSION_SECURITY_PATCH, Build.VERSION.SECURITY_PATCH);
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager) getContext().getSystemService(