Merge "Fixing the accessibility text traversal in extend mode (CTS)." into jb-mr2-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 112c8ad..a4277fc 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -77,6 +77,7 @@
CtsHardwareTestCases \
CtsHoloTestCases \
CtsJniTestCases \
+ CtsKeystoreTestCases \
CtsLocationTestCases \
CtsMediaStressTestCases \
CtsMediaTestCases \
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java
index fdd5597..913ff4f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java
@@ -120,9 +120,11 @@
@Override
public void onClick(View v) {
// Stop camera until preview sizes have been obtained.
- mCamera.stopPreview();
- mCamera.release();
- mCamera = null;
+ if (mCamera != null) {
+ mCamera.stopPreview();
+ mCamera.release();
+ mCamera = null;
+ }
mPreviewSizeCamerasToProcess.clear();
mPreviewSizes = new Size[Camera.getNumberOfCameras()];
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nls/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/nls/MockListener.java
index 62a09c9..8549214 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nls/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nls/MockListener.java
@@ -16,7 +16,6 @@
package com.android.cts.verifier.nls;
import android.app.Activity;
-import android.app.Notification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -26,6 +25,9 @@
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.util.ArrayList;
import java.util.List;
@@ -47,6 +49,13 @@
static final int RESULT_TIMEOUT = Activity.RESULT_FIRST_USER;
static final int RESULT_NO_SERVER = Activity.RESULT_FIRST_USER + 1;
+ static final String JSON_FLAGS = "flag";
+ static final String JSON_ICON = "icon";
+ static final String JSON_ID = "id";
+ static final String JSON_PACKAGE = "pkg";
+ static final String JSON_WHEN = "when";
+ static final String JSON_TAG = "tag";
+
private ArrayList<String> mPosted = new ArrayList<String>();
private ArrayList<String> mPayloads = new ArrayList<String>();
private ArrayList<String> mRemoved = new ArrayList<String>();
@@ -126,20 +135,24 @@
mPosted.clear();
mPayloads.clear();
mRemoved.clear();
- Log.d(TAG, "reset");
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
Log.d(TAG, "posted: " + sbn.getTag());
mPosted.add(sbn.getTag());
- StringBuilder payload = new StringBuilder();
- payload.append(sbn.getTag());
- payload.append(":");
- payload.append(sbn.getId());
- payload.append(":");
- payload.append(sbn.getPackageName());
- mPayloads.add(payload.toString());
+ JSONObject payload = new JSONObject();
+ try {
+ payload.put(JSON_TAG, sbn.getTag());
+ payload.put(JSON_ID, sbn.getId());
+ payload.put(JSON_PACKAGE, sbn.getPackageName());
+ payload.put(JSON_WHEN, sbn.getNotification().when);
+ payload.put(JSON_ICON, sbn.getNotification().icon);
+ payload.put(JSON_FLAGS, sbn.getNotification().flags);
+ mPayloads.add(payload.toString());
+ } catch (JSONException e) {
+ Log.e(TAG, "failed to pack up notification payload", e);
+ }
}
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nls/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nls/NotificationListenerVerifierActivity.java
index 7c64eb6..842c024 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nls/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nls/NotificationListenerVerifierActivity.java
@@ -16,6 +16,13 @@
package com.android.cts.verifier.nls;
+import static com.android.cts.verifier.nls.MockListener.JSON_FLAGS;
+import static com.android.cts.verifier.nls.MockListener.JSON_ICON;
+import static com.android.cts.verifier.nls.MockListener.JSON_ID;
+import static com.android.cts.verifier.nls.MockListener.JSON_PACKAGE;
+import static com.android.cts.verifier.nls.MockListener.JSON_TAG;
+import static com.android.cts.verifier.nls.MockListener.JSON_WHEN;
+
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Notification;
@@ -29,6 +36,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings.Secure;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -39,7 +47,12 @@
import com.android.cts.verifier.R;
import com.android.cts.verifier.nfc.TagVerifierActivity;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
@@ -49,13 +62,17 @@
private static final String STATE = "state";
private static final String LISTENER_PATH = "com.android.cts.verifier/" +
"com.android.cts.verifier.nls.MockListener";
+ private static final int SETUP = 0;
private static final int PASS = 1;
private static final int FAIL = 2;
private static final int WAIT_FOR_USER = 3;
+ private static final int CLEARED = 4;
+ private static final int READY = 5;
+ private static final int RETRY = 6;
private static final int NOTIFICATION_ID = 1001;
private static LinkedBlockingQueue<String> sDeletedQueue = new LinkedBlockingQueue<String>();
- private int mState = -1;
+ private int mState;
private int[] mStatus;
private LayoutInflater mInflater;
private ViewGroup mItemList;
@@ -67,8 +84,19 @@
private Context mContext;
private Runnable mRunner;
private View mHandler;
- private String mIdString;
private String mPackageString;
+ private int mIcon1;
+ private int mIcon2;
+ private int mIcon3;
+ private int mId1;
+ private int mId2;
+ private int mId3;
+ private long mWhen1;
+ private long mWhen2;
+ private long mWhen3;
+ private int mFlag1;
+ private int mFlag2;
+ private int mFlag3;
public static class DismissService extends Service {
@Override
@@ -87,7 +115,7 @@
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
- mState = savedInstanceState.getInt(STATE, -1);
+ mState = savedInstanceState.getInt(STATE, 0);
}
mContext = this;
mRunner = this;
@@ -115,7 +143,7 @@
@Override
protected void onResume() {
super.onResume();
- mHandler.post(mRunner);
+ next();
}
// Interface Utilities
@@ -133,15 +161,20 @@
}
private void setItemState(int index, boolean passed) {
- if (index != -1) {
- ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
- ImageView status = (ImageView) item.findViewById(R.id.nls_status);
- status.setImageResource(passed ? R.drawable.fs_good : R.drawable.fs_error);
- View button = item.findViewById(R.id.nls_launch_settings);
- button.setClickable(false);
- button.setEnabled(false);
- status.invalidate();
- }
+ ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
+ ImageView status = (ImageView) item.findViewById(R.id.nls_status);
+ status.setImageResource(passed ? R.drawable.fs_good : R.drawable.fs_error);
+ View button = item.findViewById(R.id.nls_launch_settings);
+ button.setClickable(false);
+ button.setEnabled(false);
+ status.invalidate();
+ }
+
+ private void markItemWaiting(int index) {
+ ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
+ ImageView status = (ImageView) item.findViewById(R.id.nls_status);
+ status.setImageResource(R.drawable.fs_warning);
+ status.invalidate();
}
private View createUserItem(int stringId) {
@@ -165,7 +198,7 @@
// Test management
public void run() {
- while (mState >= 0 && mState < mStatus.length && mStatus[mState] != WAIT_FOR_USER) {
+ while (mState < mStatus.length && mStatus[mState] != WAIT_FOR_USER) {
if (mStatus[mState] == PASS) {
setItemState(mState, true);
mState++;
@@ -177,11 +210,11 @@
}
}
+ if (mState < mStatus.length && mStatus[mState] == WAIT_FOR_USER) {
+ markItemWaiting(mState);
+ }
+
switch (mState) {
- case -1:
- mState++;
- mHandler.post(mRunner);
- break;
case 0:
testIsEnabled(0);
break;
@@ -211,6 +244,7 @@
break;
case 9:
getPassButton().setEnabled(true);
+ mNm.cancelAll();
break;
}
}
@@ -236,41 +270,109 @@
mNm.cancelAll();
+ mWhen1 = System.currentTimeMillis() + 1;
+ mWhen2 = System.currentTimeMillis() + 2;
+ mWhen3 = System.currentTimeMillis() + 3;
+
+ mIcon1 = R.drawable.fs_good;
+ mIcon2 = R.drawable.fs_error;
+ mIcon3 = R.drawable.fs_warning;
+
+ mId1 = NOTIFICATION_ID + 1;
+ mId2 = NOTIFICATION_ID + 2;
+ mId3 = NOTIFICATION_ID + 3;
+
+ mPackageString = "com.android.cts.verifier";
+
Notification n1 = new Notification.Builder(mContext)
.setContentTitle("ClearTest 1")
.setContentText(mTag1.toString())
.setPriority(Notification.PRIORITY_LOW)
- .setSmallIcon(R.drawable.fs_good)
+ .setSmallIcon(mIcon1)
+ .setWhen(mWhen1)
.setDeleteIntent(makeIntent(1, mTag1))
+ .setOnlyAlertOnce(true)
.build();
- mNm.notify(mTag1, NOTIFICATION_ID + 1, n1);
- mIdString = Integer.toString(NOTIFICATION_ID + 1);
- mPackageString = "com.android.cts.verifier";
+ mNm.notify(mTag1, mId1, n1);
+ mFlag1 = Notification.FLAG_ONLY_ALERT_ONCE;
Notification n2 = new Notification.Builder(mContext)
.setContentTitle("ClearTest 2")
.setContentText(mTag2.toString())
- .setPriority(Notification.PRIORITY_LOW)
- .setSmallIcon(R.drawable.fs_good)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setSmallIcon(mIcon2)
+ .setWhen(mWhen2)
.setDeleteIntent(makeIntent(2, mTag2))
+ .setAutoCancel(true)
.build();
- mNm.notify(mTag2, NOTIFICATION_ID + 2, n2);
+ mNm.notify(mTag2, mId2, n2);
+ mFlag2 = Notification.FLAG_AUTO_CANCEL;
Notification n3 = new Notification.Builder(mContext)
.setContentTitle("ClearTest 3")
.setContentText(mTag3.toString())
.setPriority(Notification.PRIORITY_LOW)
- .setSmallIcon(R.drawable.fs_good)
+ .setSmallIcon(mIcon3)
+ .setWhen(mWhen3)
.setDeleteIntent(makeIntent(3, mTag3))
+ .setAutoCancel(true)
+ .setOnlyAlertOnce(true)
.build();
- mNm.notify(mTag3, NOTIFICATION_ID + 3, n3);
+ mNm.notify(mTag3, mId3, n3);
+ mFlag3 = Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL;
+ }
+
+ /**
+ * Return to the state machine to progress through the tests.
+ */
+ private void next() {
+ mHandler.post(mRunner);
+ }
+
+ /**
+ * Wait for things to settle before returning to the state machine.
+ */
+ private void delay() {
+ mHandler.postDelayed(mRunner, 2000);
+ }
+
+ boolean checkEquals(long expected, long actual, String message) {
+ if (expected == actual) {
+ return true;
+ }
+ logWithStack(String.format(message, expected, actual));
+ return false;
+ }
+
+ boolean checkEquals(String expected, String actual, String message) {
+ if (expected.equals(actual)) {
+ return true;
+ }
+ logWithStack(String.format(message, expected, actual));
+ return false;
+ }
+
+ boolean checkFlagSet(int expected, int actual, String message) {
+ if ((expected & actual) != 0) {
+ return true;
+ }
+ logWithStack(String.format(message, expected, actual));
+ return false;
+ };
+
+ private void logWithStack(String message) {
+ Throwable stackTrace = new Throwable();
+ stackTrace.fillInStackTrace();
+ Log.e(TAG, message, stackTrace);
}
// Tests
private void testIsEnabled(int i) {
+ // no setup required
Intent settings = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
if (settings.resolveActivity(mPackageManager) == null) {
+ logWithStack("failed testIsEnabled: no settings activity");
mStatus[i] = FAIL;
} else {
// TODO: find out why Secure.ENABLED_NOTIFICATION_LISTENERS is hidden
@@ -282,139 +384,247 @@
mStatus[i] = WAIT_FOR_USER;
}
}
- mHandler.postDelayed(mRunner, 2000);
+ next();
}
private void testIsStarted(final int i) {
- MockListener.resetListenerData(this);
- MockListener.probeListenerStatus(mContext,
- new MockListener.IntegerResultCatcher() {
- @Override
- public void accept(int result) {
- if (result == Activity.RESULT_OK) {
- mStatus[i] = PASS;
- // setup for testNotificationRecieved
- sendNotificaitons();
- } else {
- mStatus[i] = FAIL;
+ if (mStatus[i] == SETUP) {
+ mStatus[i] = READY;
+ // wait for the service to start
+ delay();
+ } else {
+ MockListener.probeListenerStatus(mContext,
+ new MockListener.IntegerResultCatcher() {
+ @Override
+ public void accept(int result) {
+ if (result == Activity.RESULT_OK) {
+ mStatus[i] = PASS;
+ } else {
+ logWithStack("failed testIsStarted: " + result);
+ mStatus[i] = FAIL;
+ }
+ next();
}
- mHandler.postDelayed(mRunner, 2000);
- }
- });
+ });
+ }
}
private void testNotificationRecieved(final int i) {
- MockListener.probeListenerPosted(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- if (result.size() > 0 && result.contains(mTag1)) {
- mStatus[i] = PASS;
- } else {
- mStatus[i] = FAIL;
- }
- mHandler.post(mRunner);
- }});
+ if (mStatus[i] == SETUP) {
+ MockListener.resetListenerData(this);
+ mStatus[i] = CLEARED;
+ // wait for intent to move through the system
+ delay();
+ } else if (mStatus[i] == CLEARED) {
+ sendNotificaitons();
+ mStatus[i] = READY;
+ // wait for notifications to move through the system
+ delay();
+ } else {
+ MockListener.probeListenerPosted(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ if (result.size() > 0 && result.contains(mTag1)) {
+ mStatus[i] = PASS;
+ } else {
+ logWithStack("failed testNotificationRecieved");
+ mStatus[i] = FAIL;
+ }
+ next();
+ }});
+ }
}
private void testDataIntact(final int i) {
+ // no setup required
MockListener.probeListenerPayloads(mContext,
new MockListener.StringListResultCatcher() {
@Override
public void accept(List<String> result) {
- mStatus[i] = FAIL;
+ boolean pass = false;
+ Set<String> found = new HashSet<String>();
if (result.size() > 0) {
- for(String payload : result) {
- if (payload.contains(mTag1) &&
- payload.contains(mIdString) &&
- payload.contains(mPackageString)) {
- mStatus[i] = PASS;
+ pass = true;
+ for(String payloadData : result) {
+ try {
+ JSONObject payload = new JSONObject(payloadData);
+ pass &= checkEquals(mPackageString, payload.getString(JSON_PACKAGE),
+ "data integrity test fail: notificaiton package (%s, %s)");
+ String tag = payload.getString(JSON_TAG);
+ if (mTag1.equals(tag)) {
+ found.add(mTag1);
+ pass &= checkEquals(mIcon1, payload.getInt(JSON_ICON),
+ "data integrity test fail: notificaiton icon (%d, %d)");
+ pass &= checkFlagSet(mFlag1, payload.getInt(JSON_FLAGS),
+ "data integrity test fail: notificaiton flags (%d, %d)");
+ pass &= checkEquals(mId1, payload.getInt(JSON_ID),
+ "data integrity test fail: notificaiton ID (%d, %d)");
+ pass &= checkEquals(mWhen1, payload.getLong(JSON_WHEN),
+ "data integrity test fail: notificaiton when (%d, %d)");
+ } else if (mTag2.equals(tag)) {
+ found.add(mTag2);
+ pass &= checkEquals(mIcon2, payload.getInt(JSON_ICON),
+ "data integrity test fail: notificaiton icon (%d, %d)");
+ pass &= checkFlagSet(mFlag2, payload.getInt(JSON_FLAGS),
+ "data integrity test fail: notificaiton flags (%d, %d)");
+ pass &= checkEquals(mId2, payload.getInt(JSON_ID),
+ "data integrity test fail: notificaiton ID (%d, %d)");
+ pass &= checkEquals(mWhen2, payload.getLong(JSON_WHEN),
+ "data integrity test fail: notificaiton when (%d, %d)");
+ } else if (mTag3.equals(tag)) {
+ found.add(mTag3);
+ pass &= checkEquals(mIcon3, payload.getInt(JSON_ICON),
+ "data integrity test fail: notificaiton icon (%d, %d)");
+ pass &= checkFlagSet(mFlag3, payload.getInt(JSON_FLAGS),
+ "data integrity test fail: notificaiton flags (%d, %d)");
+ pass &= checkEquals(mId3, payload.getInt(JSON_ID),
+ "data integrity test fail: notificaiton ID (%d, %d)");
+ pass &= checkEquals(mWhen3, payload.getLong(JSON_WHEN),
+ "data integrity test fail: notificaiton when (%d, %d)");
+ } else {
+ pass = false;
+ logWithStack("failed on unexpected notification tag: " + tag);
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
}
}
}
- // setup for testDismissOne
- MockListener.resetListenerData(mContext);
- MockListener.clearOne(mContext, mTag1, NOTIFICATION_ID + 1);
- mHandler.postDelayed(mRunner, 1000);
+ pass &= found.size() == 3;
+ mStatus[i] = pass ? PASS : FAIL;
+ next();
}});
}
private void testDismissOne(final int i) {
- MockListener.probeListenerRemoved(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- if (result.size() > 0 && result.contains(mTag1)) {
- mStatus[i] = PASS;
- } else {
- mStatus[i] = FAIL;
- }
-
- // setup for testDismissAll
- MockListener.resetListenerData(mContext);
- MockListener.clearAll(mContext);
- mHandler.postDelayed(mRunner, 1000);
- }});
+ if (mStatus[i] == SETUP) {
+ MockListener.resetListenerData(this);
+ mStatus[i] = CLEARED;
+ // wait for intent to move through the system
+ delay();
+ } else if (mStatus[i] == CLEARED) {
+ MockListener.clearOne(mContext, mTag1, NOTIFICATION_ID + 1);
+ mStatus[i] = READY;
+ delay();
+ } else {
+ MockListener.probeListenerRemoved(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ if (result.size() > 0 && result.contains(mTag1)) {
+ mStatus[i] = PASS;
+ next();
+ } else {
+ if (mStatus[i] == RETRY) {
+ logWithStack("failed testDismissOne");
+ mStatus[i] = FAIL;
+ next();
+ } else {
+ logWithStack("failed testDismissOne, once: retrying");
+ mStatus[i] = RETRY;
+ delay();
+ }
+ }
+ }});
+ }
}
private void testDismissAll(final int i) {
- MockListener.probeListenerRemoved(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- if (result.size() == 2 && result.contains(mTag2) && result.contains(mTag3)) {
- mStatus[i] = PASS;
- } else {
- mStatus[i] = FAIL;
+ if (mStatus[i] == SETUP) {
+ MockListener.resetListenerData(this);
+ mStatus[i] = CLEARED;
+ // wait for intent to move through the system
+ delay();
+ } else if (mStatus[i] == CLEARED) {
+ MockListener.clearAll(mContext);
+ mStatus[i] = READY;
+ delay();
+ } else {
+ MockListener.probeListenerRemoved(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ if (result.size() == 2 && result.contains(mTag2) && result.contains(mTag3)) {
+ mStatus[i] = PASS;
+ next();
+ } else {
+ if (mStatus[i] == RETRY) {
+ logWithStack("failed testDismissAll");
+ mStatus[i] = FAIL;
+ next();
+ } else {
+ logWithStack("failed testDismissAll, once: retrying");
+ mStatus[i] = RETRY;
+ delay();
+ }
+ }
}
- mHandler.post(mRunner);
- }
- });
+ });
+ }
}
private void testIsDisabled(int i) {
- MockListener.resetListenerData(this);
+ // no setup required
// TODO: find out why Secure.ENABLED_NOTIFICATION_LISTENERS is hidden
String listeners = Secure.getString(getContentResolver(),
"enabled_notification_listeners");
if (listeners == null || !listeners.contains(LISTENER_PATH)) {
mStatus[i] = PASS;
+ next();
} else {
mStatus[i] = WAIT_FOR_USER;
+ delay();
}
- mHandler.postDelayed(mRunner, 2000);
}
private void testIsStopped(final int i) {
- MockListener.probeListenerStatus(mContext,
- new MockListener.IntegerResultCatcher() {
- @Override
- public void accept(int result) {
- if (result == Activity.RESULT_OK) {
- MockListener.resetListenerData(mContext);
- sendNotificaitons();
- mStatus[i] = FAIL;
- } else {
- mStatus[i] = PASS;
+ if (mStatus[i] == SETUP) {
+ mStatus[i] = READY;
+ // wait for the service to start
+ delay();
+ } else {
+ MockListener.probeListenerStatus(mContext,
+ new MockListener.IntegerResultCatcher() {
+ @Override
+ public void accept(int result) {
+ if (result == Activity.RESULT_OK) {
+ logWithStack("failed testIsStopped");
+ mStatus[i] = FAIL;
+ } else {
+ mStatus[i] = PASS;
+ }
+ next();
}
- // setup for testNotificationRecieved
- sendNotificaitons();
- mHandler.postDelayed(mRunner, 1000);
- }
- });
+ });
+ }
}
private void testNotificationNotRecieved(final int i) {
- MockListener.probeListenerPosted(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- if (result == null || result.size() == 0) {
- mStatus[i] = PASS;
- } else {
- mStatus[i] = FAIL;
- }
- mHandler.post(mRunner);
- }});
+ if (mStatus[i] == SETUP) {
+ MockListener.resetListenerData(this);
+ mStatus[i] = CLEARED;
+ // wait for intent to move through the system
+ delay();
+ } else if (mStatus[i] == CLEARED) {
+ // setup for testNotificationRecieved
+ sendNotificaitons();
+ mStatus[i] = READY;
+ delay();
+ } else {
+ MockListener.probeListenerPosted(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ if (result == null || result.size() == 0) {
+ mStatus[i] = PASS;
+ } else {
+ logWithStack("failed testNotificationNotRecieved");
+ mStatus[i] = FAIL;
+ }
+ next();
+ }});
+ }
}
}
diff --git a/suite/pts/deviceTests/opengl/README b/suite/pts/deviceTests/opengl/README
new file mode 100644
index 0000000..ce5e8e8
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/README
@@ -0,0 +1,13 @@
+Copyright (C) 2013 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+in compliance with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License
+is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+or implied. See the License for the specific language governing permissions and limitations under
+the License.
+
+All the assets in this project are under the above license and were made using Gimp and/or Blender.
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/assets/fragment/basic b/suite/pts/deviceTests/opengl/assets/fragment/basic
new file mode 100644
index 0000000..69d98b0
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/fragment/basic
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+precision mediump float;
+uniform sampler2D u_Texture;
+varying vec2 v_TexCoordinate;
+void main() {
+ gl_FragColor = texture2D(u_Texture, v_TexCoordinate);
+}
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/assets/fragment/blur b/suite/pts/deviceTests/opengl/assets/fragment/blur
new file mode 100644
index 0000000..1290798
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/fragment/blur
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+precision mediump float;
+uniform sampler2D u_Texture;
+uniform vec2 u_Scale;
+varying vec2 v_TexCoordinate;
+void main() {
+ float weights[11];
+ weights[0] = 0.047748641153356156;
+ weights[1] = 0.05979670798364139;
+ weights[2] = 0.07123260215138659;
+ weights[3] = 0.08071711293576822;
+ weights[4] = 0.08700369673862933;
+ weights[5] = 0.08920620580763855;
+ weights[6] = 0.08700369673862933;
+ weights[7] = 0.08071711293576822;
+ weights[8] = 0.07123260215138659;
+ weights[9] = 0.05979670798364139;
+ weights[10] = 0.047748641153356156;
+ vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
+ for (int i = 0; i < 11; i++) {
+ vec2 coords = v_TexCoordinate.xy + ((float(i) - 5.0) * u_Scale);
+ color += texture2D(u_Texture, coords) * weights[i];
+ }
+ gl_FragColor = color;
+}
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/assets/fragment/perspective b/suite/pts/deviceTests/opengl/assets/fragment/perspective
new file mode 100644
index 0000000..8546938
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/fragment/perspective
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+precision mediump float;
+uniform vec3 u_LightPos;
+uniform sampler2D u_Texture;
+varying vec3 v_Position;
+varying vec3 v_Normal;
+varying vec2 v_TexCoordinate;
+void main() {
+ // Get a lighting direction vector from the light to the vertex.
+ vec3 lightVector = normalize(u_LightPos - v_Position);
+ // Calculate the dot product of the light vector and vertex normal.
+ float diffuse = max(dot(lightVector, v_Normal), 0.0);
+ // Add ambient lighting
+ diffuse = diffuse + 0.25;
+ // Multiply the diffuse illumination and texture to get final output color.
+ gl_FragColor = (diffuse * texture2D(u_Texture, v_TexCoordinate));
+}
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/assets/fragment/water b/suite/pts/deviceTests/opengl/assets/fragment/water
new file mode 100644
index 0000000..26d0954
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/fragment/water
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+precision mediump float;
+uniform vec3 u_LightPos;
+uniform sampler2D u_Texture1;
+uniform sampler2D u_Texture2;
+uniform int u_Time;
+varying vec3 v_Position;
+varying vec2 v_TexCoordinate;
+void main() {
+ float weight = abs(mod(float(u_Time), 101.0) - 50.0) / 50.0;// loop between 0.0 and 1.0
+ float offset = abs(float(u_Time) / 1000.0);
+ // Get normal from bump map.
+ vec3 map1 = texture2D(u_Texture1, v_TexCoordinate + offset).xyz * 2.0 - 1.0;
+ vec3 map2 = texture2D(u_Texture2, v_TexCoordinate + offset).xyz * 2.0 - 1.0;
+ vec3 normal = normalize((map1 * weight) + (map2 * (1.0 - weight)));
+ // Get a lighting direction vector from the light to the vertex.
+ vec3 lightVector = normalize(u_LightPos - v_Position);
+ // Calculate the dot product of the light vector and vertex normal.
+ float diffuse = max(dot(lightVector, normal), 0.0);
+ // Add ambient lighting
+ diffuse = diffuse + 0.025;
+ // Use the diffuse illumination to get final output color.
+ gl_FragColor = vec4(1.0 - diffuse, 1.0 - diffuse, 1.0, 1.0 - (diffuse * 0.9));// Semi transparent blue.
+}
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/assets/mesh/arc.cob b/suite/pts/deviceTests/opengl/assets/mesh/arc.cob
new file mode 100644
index 0000000..1defaf2
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/mesh/arc.cob
Binary files differ
diff --git a/suite/pts/deviceTests/opengl/assets/mesh/fish.cob b/suite/pts/deviceTests/opengl/assets/mesh/fish.cob
new file mode 100644
index 0000000..16de76c
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/mesh/fish.cob
Binary files differ
diff --git a/suite/pts/deviceTests/opengl/assets/mesh/plane.cob b/suite/pts/deviceTests/opengl/assets/mesh/plane.cob
new file mode 100644
index 0000000..b6c62d9
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/mesh/plane.cob
Binary files differ
diff --git a/suite/pts/deviceTests/opengl/assets/texture/arc.png b/suite/pts/deviceTests/opengl/assets/texture/arc.png
new file mode 100644
index 0000000..5a68acd
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/texture/arc.png
Binary files differ
diff --git a/suite/pts/deviceTests/opengl/assets/texture/background.png b/suite/pts/deviceTests/opengl/assets/texture/background.png
new file mode 100644
index 0000000..db0d472
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/texture/background.png
Binary files differ
diff --git a/suite/pts/deviceTests/opengl/assets/texture/fish.png b/suite/pts/deviceTests/opengl/assets/texture/fish.png
new file mode 100644
index 0000000..6f62650
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/texture/fish.png
Binary files differ
diff --git a/suite/pts/deviceTests/opengl/assets/texture/fish_dark.png b/suite/pts/deviceTests/opengl/assets/texture/fish_dark.png
new file mode 100644
index 0000000..54453a2
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/texture/fish_dark.png
Binary files differ
diff --git a/suite/pts/deviceTests/opengl/assets/texture/water1.png b/suite/pts/deviceTests/opengl/assets/texture/water1.png
new file mode 100644
index 0000000..2fdefa1
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/texture/water1.png
Binary files differ
diff --git a/suite/pts/deviceTests/opengl/assets/texture/water2.png b/suite/pts/deviceTests/opengl/assets/texture/water2.png
new file mode 100644
index 0000000..7050e3d
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/texture/water2.png
Binary files differ
diff --git a/suite/pts/deviceTests/opengl/assets/vertex/basic b/suite/pts/deviceTests/opengl/assets/vertex/basic
new file mode 100644
index 0000000..be831d0
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/vertex/basic
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+attribute vec4 a_Position;
+attribute vec2 a_TexCoordinate;
+varying vec2 v_TexCoordinate;
+void main() {
+ // Pass through the texture coordinate.
+ v_TexCoordinate = a_TexCoordinate;
+ // Set the position on screen.
+ gl_Position = a_Position;
+}
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/assets/vertex/blur b/suite/pts/deviceTests/opengl/assets/vertex/blur
new file mode 100644
index 0000000..ffb2bf0
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/vertex/blur
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+attribute vec4 a_Position;
+attribute vec2 a_TexCoordinate;
+varying vec2 v_TexCoordinate;
+void main() {
+ // Set the position.
+ gl_Position = a_Position;
+ // Pass through the texture coordinate.
+ v_TexCoordinate = a_TexCoordinate;
+}
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/assets/vertex/perspective b/suite/pts/deviceTests/opengl/assets/vertex/perspective
new file mode 100644
index 0000000..6889c72
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/vertex/perspective
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+uniform mat4 u_MVPMatrix;
+uniform mat4 u_MVMatrix;
+attribute vec4 a_Position;
+attribute vec3 a_Normal;
+attribute vec2 a_TexCoordinate;
+varying vec3 v_Position;
+varying vec3 v_Normal;
+varying vec2 v_TexCoordinate;
+void main() {
+ // Transform the vertex into eye space.
+ v_Position = vec3(u_MVMatrix * a_Position);
+ // Pass through the texture coordinate.
+ v_TexCoordinate = a_TexCoordinate;
+ // Transform the normal\'s orientation into eye space.
+ v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));
+ // Multiply to get the final point in normalized screen coordinates.
+ gl_Position = u_MVPMatrix * a_Position;
+}
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/assets/vertex/water b/suite/pts/deviceTests/opengl/assets/vertex/water
new file mode 100644
index 0000000..05174e4
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/assets/vertex/water
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+uniform mat4 u_MVPMatrix;
+uniform mat4 u_MVMatrix;
+attribute vec4 a_Position;
+attribute vec2 a_TexCoordinate;
+varying vec3 v_Position;
+varying vec2 v_TexCoordinate;
+void main() {
+ // Transform the vertex into eye space.
+ v_Position = vec3(u_MVMatrix * a_Position);
+ // Pass through the texture coordinate.
+ v_TexCoordinate = a_TexCoordinate;
+ // Multiply to get the final point in normalized screen coordinates.
+ gl_Position = u_MVPMatrix * a_Position;
+}
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/cob_exporter.py b/suite/pts/deviceTests/opengl/cob_exporter.py
new file mode 100644
index 0000000..0a210c9
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/cob_exporter.py
@@ -0,0 +1,75 @@
+import os, bpy, struct
+from bpy_extras.io_utils import ExportHelper
+from bpy.props import StringProperty, BoolProperty, EnumProperty
+
+# This is a custom Blender export script to output an object to a format that's easy to use in
+# android. While open gl allows the use of an index buffer for vertices, the same cannot be done
+# for texture coordinates, which is why this script duplicates the vertices and normals. This
+# gives a larger but faster loading file, hence the tongue-in-cheek name "Compressed" object file.
+# The format is number of vertices + list of vertices (3 coord, 3 normal, 2 texcoord)
+bl_info = {
+ "name": "COB Exporter",
+ "description": "Exports the active scene into a Compressed Object file.",
+ "author": "Stuart Scott",
+ "version": (1, 0, 0),
+ "blender": (2, 6, 2),
+ "api": 36339,
+ "location": "File > Export > COB Exporter (.cob)",
+ "warning": "", # used for warning icon and text in addons panel
+ "wiki_url": "",
+ "tracker_url": "",
+ "category": "Import-Export"
+ }
+
+class COBExporter(bpy.types.Operator, ExportHelper):
+ '''Exports the current scene into a Compressed Object format.'''
+ bl_idname = "export.cob_exporter" # this is important since its how bpy.ops.export.cob_exporter is constructed
+ bl_label = "COB Exporter"
+
+ filename_ext = ".cob"
+
+ filter_glob = StringProperty(default="*.cob", options={'HIDDEN'})
+
+ def execute(self, context):
+ mesh = context.active_object
+ if mesh.type != 'MESH':
+ print("Active object is not a mesh")
+ return {'FINISHED'}
+ if mesh.mode != 'OBJECT':
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+ print("Writing "+mesh.name+" to "+self.filepath)
+ uvtex = mesh.data.uv_textures.active # points to active texture
+ f = open(self.filepath, 'wb')
+ f.write(struct.pack(">i", len(uvtex.data) * 3))# write length
+ for uv_index, uv_itself in enumerate(uvtex.data):
+ # get uv for this face
+ uvs = uv_itself.uv1, uv_itself.uv2, uv_itself.uv3
+ for vertex_index, vertex_itself in enumerate(mesh.data.faces[uv_index].vertices):
+ # for each vertex in the face
+ vertex = mesh.data.vertices[vertex_itself]
+ v = vertex.co.xyz
+ n = vertex.normal.xyz
+ t = uvs[vertex_index]
+ f.write(struct.pack(">ffffffff", v[0], v[1], v[2], n[0], n[1], n[2], t[0], 1 - t[1]))
+ f.close()
+ return {'FINISHED'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_object != None
+
+# Only needed if you want to add into a dynamic menu
+def menu_func(self, context):
+ default_path = os.path.splitext(bpy.data.filepath)[0] + ".cob"
+ self.layout.operator(COBExporter.bl_idname, text="Compressed Object (.cob)").filepath = default_path
+
+def register():
+ bpy.utils.register_module(__name__)
+ bpy.types.INFO_MT_file_export.append(menu_func)
+
+def unregister():
+ bpy.utils.unregister_module(__name__)
+ bpy.types.INFO_MT_file_export.remove(menu_func)
+
+if __name__ == "__main__":
+ register()
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.cpp b/suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.cpp
deleted file mode 100644
index 273517a..0000000
--- a/suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License
- * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing permissions and limitations under
- * the License.
- */
-
-#include "BasicMeshNode.h"
-
-#include "BasicProgram.h"
-
-BasicMeshNode::BasicMeshNode(const Mesh* mesh, const GLuint textureId) :
- MeshNode(mesh),
- mTextureId(textureId) {
-}
-
-void BasicMeshNode::before(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
- BasicProgram& prog = (BasicProgram&) program;
-
- glActiveTexture(GL_TEXTURE0);
- // Bind the texture to this unit.
- glBindTexture(GL_TEXTURE_2D, mTextureId);
- // Tell the texture uniform sampler to use this texture in the shader by binding to texture
- // unit 0.
- glUniform1i(prog.mTextureUniformHandle, 0);
-
- glEnableVertexAttribArray(prog.mPositionHandle);
- glEnableVertexAttribArray(prog.mNormalHandle);
- glEnableVertexAttribArray(prog.mTexCoordHandle);
- glVertexAttribPointer(prog.mPositionHandle, 3, GL_FLOAT, false, 0, mMesh->mVertices);
- glVertexAttribPointer(prog.mNormalHandle, 3, GL_FLOAT, false, 0, mMesh->mNormals);
- glVertexAttribPointer(prog.mTexCoordHandle, 2, GL_FLOAT, false, 0, mMesh->mTexCoords);
-
- // This multiplies the view matrix by the model matrix, and stores the result in the MVP
- // matrix (which currently contains model * view).
- prog.mMVMatrix.multiply(view, model);
-
- // Pass in the modelview matrix.
- glUniformMatrix4fv(prog.mMVMatrixHandle, 1, false, prog.mMVMatrix.mData);
-
- // This multiplies the modelview matrix by the projection matrix, and stores the result in
- // the MVP matrix (which now contains model * view * projection).
- prog.mMVPMatrix.multiply(projection, prog.mMVMatrix);
-
- // Pass in the combined matrix.
- glUniformMatrix4fv(prog.mMVPMatrixHandle, 1, false, prog.mMVPMatrix.mData);
-
- // Pass in the light position in eye space.
- glUniform3f(prog.mLightPosHandle, prog.mLightPosInEyeSpace[0], prog.mLightPosInEyeSpace[1],
- prog.mLightPosInEyeSpace[2]);
-
- glDrawArrays(GL_TRIANGLES, 0, mMesh->mNumVertices);
-}
-
-void BasicMeshNode::after(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
-}
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/GLUtils.cpp b/suite/pts/deviceTests/opengl/jni/graphics/GLUtils.cpp
index 2484153..c4fbf7f 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/GLUtils.cpp
+++ b/suite/pts/deviceTests/opengl/jni/graphics/GLUtils.cpp
@@ -16,10 +16,116 @@
#include <stdlib.h>
#include <sys/time.h>
+#include <android/asset_manager_jni.h>
+
#define LOG_TAG "PTS_OPENGL"
#define LOG_NDEBUG 0
#include <utils/Log.h>
+static JNIEnv* sEnv = NULL;
+static jobject sAssetManager = NULL;
+
+void GLUtils::setEnvAndAssetManager(JNIEnv* env, jobject assetManager) {
+ sEnv = env;
+ sAssetManager = assetManager;
+}
+
+static AAsset* loadAsset(const char* path) {
+ AAssetManager* nativeManager = AAssetManager_fromJava(sEnv, sAssetManager);
+ if (nativeManager == NULL) {
+ return NULL;
+ }
+ return AAssetManager_open(nativeManager, path, AASSET_MODE_UNKNOWN);;
+}
+
+char* GLUtils::openTextFile(const char* path) {
+ AAsset* asset = loadAsset(path);
+ if (asset == NULL) {
+ ALOGE("Couldn't load %s", path);
+ return NULL;
+ }
+ off_t length = AAsset_getLength(asset);
+ char* buffer = new char[length + 1];
+ int num = AAsset_read(asset, buffer, length);
+ AAsset_close(asset);
+ if (num != length) {
+ ALOGE("Couldn't read %s", path);
+ delete[] buffer;
+ return NULL;
+ }
+ buffer[length] = '\0';
+ return buffer;
+}
+
+GLuint GLUtils::loadTexture(const char* path) {
+ GLuint textureId = 0;
+ jclass activityClass = sEnv->FindClass("com/android/pts/opengl/reference/GLGameActivity");
+ if (activityClass == NULL) {
+ ALOGE("Couldn't find activity class");
+ return -1;
+ }
+ jmethodID loadTexture = sEnv->GetStaticMethodID(activityClass, "loadTexture",
+ "(Landroid/content/res/AssetManager;Ljava/lang/String;)I");
+ if (loadTexture == NULL) {
+ ALOGE("Couldn't find loadTexture method");
+ return -1;
+ }
+ jstring pathStr = sEnv->NewStringUTF(path);
+ textureId = sEnv->CallStaticIntMethod(activityClass, loadTexture, sAssetManager, pathStr);
+ sEnv->DeleteLocalRef(pathStr);
+ return textureId;
+}
+
+static int readInt(char* b) {
+ return (((int) b[0]) << 24) | (((int) b[1]) << 16) | (((int) b[2]) << 8) | ((int) b[3]);
+}
+
+static float readFloat(char* b) {
+ union {
+ int input;
+ float output;
+ } data;
+ data.input = readInt(b);
+ return data.output;
+}
+
+Mesh* GLUtils::loadMesh(const char* path) {
+ char* buffer = openTextFile(path);
+ if (buffer == NULL) {
+ return NULL;
+ }
+ int index = 0;
+ int numVertices = readInt(buffer + index);
+ index += 4;
+ float* vertices = new float[numVertices * 3];
+ float* normals = new float[numVertices * 3];
+ float* texCoords = new float[numVertices * 2];
+ for (int i = 0; i < numVertices; i++) {
+ // Vertices
+ int vIndex = i * 3;
+ vertices[vIndex + 0] = readFloat(buffer + index);
+ index += 4;
+ vertices[vIndex + 1] = readFloat(buffer + index);
+ index += 4;
+ vertices[vIndex + 2] = readFloat(buffer + index);
+ index += 4;
+ // Normals
+ normals[vIndex + 0] = readFloat(buffer + index);
+ index += 4;
+ normals[vIndex + 1] = readFloat(buffer + index);
+ index += 4;
+ normals[vIndex + 2] = readFloat(buffer + index);
+ index += 4;
+ // Texture Coordinates
+ int tIndex = i * 2;
+ texCoords[tIndex + 0] = readFloat(buffer + index);
+ index += 4;
+ texCoords[tIndex + 1] = readFloat(buffer + index);
+ index += 4;
+ }
+ return new Mesh(vertices, normals, texCoords, numVertices);
+}
+
// Loads the given source code as a shader of the given type.
static GLuint loadShader(GLenum shaderType, const char** source) {
GLuint shader = glCreateShader(shaderType);
@@ -44,8 +150,7 @@
return shader;
}
-GLuint GLUtils::createProgram(const char** vertexSource,
- const char** fragmentSource) {
+GLuint GLUtils::createProgram(const char** vertexSource, const char** fragmentSource) {
GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource);
if (!vertexShader) {
return 0;
@@ -120,8 +225,7 @@
}
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
- GL_UNSIGNED_BYTE, m);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/GLUtils.h b/suite/pts/deviceTests/opengl/jni/graphics/GLUtils.h
index 16060ed..6aef87c 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/GLUtils.h
+++ b/suite/pts/deviceTests/opengl/jni/graphics/GLUtils.h
@@ -14,15 +14,25 @@
#ifndef GLUTILS_H
#define GLUTILS_H
+#include <jni.h>
+
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include "Mesh.h"
+
class GLUtils {
public:
+ static void setEnvAndAssetManager(JNIEnv* env, jobject assetManager);
+ // Loads a file from assets/path into a char array.
+ static char* openTextFile(const char* path);
+ // Loads a texture from assets/texture/<name>
+ static GLuint loadTexture(const char* name);
+ // Loads a mesh from assets/mesh/<name>
+ static Mesh* loadMesh(const char* name);
// Creates a program with the given vertex and fragment shader source code.
- static GLuint createProgram(const char** vertexSource,
- const char** fragmentSource);
+ static GLuint createProgram(const char** vertexSource, const char** fragmentSource);
static double currentTimeMillis();
// Rounds a number up to the smallest power of 2 that is greater than the original number.
static int roundUpToSmallestPowerOf2(int x);
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/Matrix.cpp b/suite/pts/deviceTests/opengl/jni/graphics/Matrix.cpp
index 83591e8..422c93c 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/Matrix.cpp
+++ b/suite/pts/deviceTests/opengl/jni/graphics/Matrix.cpp
@@ -14,7 +14,7 @@
#include "Matrix.h"
#include <string.h>
-#include <math.h>
+#include <cmath>
#define LOG_TAG "PTS_OPENGL"
#define LOG_NDEBUG 0
@@ -93,8 +93,8 @@
delete temp;
}
-void Matrix::rotate(float degrees, float x, float y, float z) {
- Matrix* m = newRotate(degrees, x, y, z);
+void Matrix::rotate(float radians, float x, float y, float z) {
+ Matrix* m = newRotate(radians, x, y, z);
Matrix* temp = new Matrix(*this);
if (m != NULL && temp != NULL) {
multiply(*temp, *m);
@@ -242,7 +242,7 @@
}
return m;
}
-Matrix* Matrix::newRotate(float degrees, float x, float y, float z) {
+Matrix* Matrix::newRotate(float radians, float x, float y, float z) {
Matrix* m = new Matrix();
if (m != NULL) {
float* d = m->mData;
@@ -253,7 +253,6 @@
d[13] = 0;
d[14] = 0;
d[15] = 1;
- float radians = degrees * (M_PI / 180.0f);
float s = (float) sinf(radians);
float c = (float) cosf(radians);
if (1.0f == x && 0.0f == y && 0.0f == z) {
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/Matrix.h b/suite/pts/deviceTests/opengl/jni/graphics/Matrix.h
index 3484266..11fc23c 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/Matrix.h
+++ b/suite/pts/deviceTests/opengl/jni/graphics/Matrix.h
@@ -31,7 +31,7 @@
// Scales this matrix by the given amounts.
void scale(float x, float y, float z);
// Rotates this matrix the given angle.
- void rotate(float degrees, float x, float y, float z);
+ void rotate(float radians, float x, float y, float z);
// Sets this matrix to be the result of multiplying the given matrices.
void multiply(const Matrix& l, const Matrix& r);
@@ -48,7 +48,7 @@
// Returns a new matrix representing the scaling.
static Matrix* newScale(float x, float y, float z);
// Returns a new matrix representing the rotation.
- static Matrix* newRotate(float degrees, float x, float y, float z);
+ static Matrix* newRotate(float radians, float x, float y, float z);
// Sets the given matrix to be the result of multiplying the given matrix by the given vector.
static void multiplyVector(float* result, const Matrix& lhs,
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/PerspectiveMeshNode.cpp b/suite/pts/deviceTests/opengl/jni/graphics/PerspectiveMeshNode.cpp
new file mode 100644
index 0000000..08d2030
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/graphics/PerspectiveMeshNode.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "PerspectiveMeshNode.h"
+
+#include "PerspectiveProgram.h"
+
+PerspectiveMeshNode::PerspectiveMeshNode(const Mesh* mesh, const GLuint textureId) :
+ TexturedMeshNode(mesh, textureId) {
+}
+
+void PerspectiveMeshNode::before(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
+ PerspectiveProgram& prog = (PerspectiveProgram&) program;
+
+ int textureUniformHandle = glGetUniformLocation(prog.mProgramId, "u_Texture");
+ int positionHandle = glGetAttribLocation(prog.mProgramId, "a_Position");
+ int normalHandle = glGetAttribLocation(prog.mProgramId, "a_Normal");
+ int texCoordHandle = glGetAttribLocation(prog.mProgramId, "a_TexCoordinate");
+
+ glActiveTexture (GL_TEXTURE0);
+ // Bind the texture to this unit.
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+
+ // Tell the texture uniform sampler to use this texture in the shader by binding to texture
+ // unit 0.
+ glUniform1i(textureUniformHandle, 0);
+
+ glEnableVertexAttribArray(positionHandle);
+ glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, 0, mMesh->mVertices);
+ glEnableVertexAttribArray(normalHandle);
+ glVertexAttribPointer(normalHandle, 3, GL_FLOAT, false, 0, mMesh->mNormals);
+ glEnableVertexAttribArray(texCoordHandle);
+ glVertexAttribPointer(texCoordHandle, 2, GL_FLOAT, false, 0, mMesh->mTexCoords);
+
+ // This multiplies the view matrix by the model matrix, and stores the result in the MVP
+ // matrix (which currently contains model * view).
+ prog.mMVMatrix.multiply(view, model);
+
+ // Pass in the modelview matrix.
+ glUniformMatrix4fv(prog.mMVMatrixHandle, 1, false, prog.mMVMatrix.mData);
+
+ // This multiplies the modelview matrix by the projection matrix, and stores the result in
+ // the MVP matrix (which now contains model * view * projection).
+ prog.mMVPMatrix.multiply(projection, prog.mMVMatrix);
+
+ // Pass in the combined matrix.
+ glUniformMatrix4fv(prog.mMVPMatrixHandle, 1, false, prog.mMVPMatrix.mData);
+
+ // Pass in the light position in eye space.
+ glUniform3f(prog.mLightPosHandle, prog.mLightPosInEyeSpace[0], prog.mLightPosInEyeSpace[1],
+ prog.mLightPosInEyeSpace[2]);
+
+ glDrawArrays(GL_TRIANGLES, 0, mMesh->mNumVertices);
+}
+
+void PerspectiveMeshNode::after(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
+}
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.h b/suite/pts/deviceTests/opengl/jni/graphics/PerspectiveMeshNode.h
similarity index 72%
copy from suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.h
copy to suite/pts/deviceTests/opengl/jni/graphics/PerspectiveMeshNode.h
index 97cd24b..bc89248 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.h
+++ b/suite/pts/deviceTests/opengl/jni/graphics/PerspectiveMeshNode.h
@@ -12,24 +12,21 @@
* the License.
*/
-#ifndef BASICMESHNODE_H
-#define BASICMESHNODE_H
+#ifndef PERSPECTIVEMESHNODE_H
+#define PERSPECTIVEMESHNODE_H
#include "Matrix.h"
#include "Mesh.h"
-#include "MeshNode.h"
#include "Program.h"
+#include "TexturedMeshNode.h"
-class BasicMeshNode: public MeshNode {
+class PerspectiveMeshNode: public TexturedMeshNode {
public:
- BasicMeshNode(const Mesh* mesh, const GLuint textureId);
- virtual ~BasicMeshNode() {};
+ PerspectiveMeshNode(const Mesh* mesh, const GLuint textureId);
+ virtual ~PerspectiveMeshNode() {};
protected:
- virtual void before(Program& program, Matrix& model, Matrix& view,
- Matrix& projection);
- virtual void after(Program& program, Matrix& model, Matrix& view,
- Matrix& projection);
- const GLuint mTextureId;
+ virtual void before(Program& program, Matrix& model, Matrix& view, Matrix& projection);
+ virtual void after(Program& program, Matrix& model, Matrix& view, Matrix& projection);
};
#endif
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/BasicProgram.cpp b/suite/pts/deviceTests/opengl/jni/graphics/PerspectiveProgram.cpp
similarity index 71%
rename from suite/pts/deviceTests/opengl/jni/graphics/BasicProgram.cpp
rename to suite/pts/deviceTests/opengl/jni/graphics/PerspectiveProgram.cpp
index 2b5ebbd..7665e5a 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/BasicProgram.cpp
+++ b/suite/pts/deviceTests/opengl/jni/graphics/PerspectiveProgram.cpp
@@ -12,28 +12,24 @@
* the License.
*/
-#include "BasicProgram.h"
+#include "PerspectiveProgram.h"
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-BasicProgram::BasicProgram(GLuint programId) :
+PerspectiveProgram::PerspectiveProgram(GLuint programId) :
Program(programId) {
mLightPosInModelSpace[0] = 0.0f;
- mLightPosInModelSpace[1] = 2.0f;
- mLightPosInModelSpace[2] = 2.0f;
+ mLightPosInModelSpace[1] = 0.0f;
+ mLightPosInModelSpace[2] = 1.0f;
mLightPosInModelSpace[3] = 2.0f;
mMVMatrixHandle = glGetUniformLocation(programId, "u_MVMatrix");
mMVPMatrixHandle = glGetUniformLocation(programId, "u_MVPMatrix");
mLightPosHandle = glGetUniformLocation(programId, "u_LightPos");
- mTextureUniformHandle = glGetUniformLocation(programId, "u_Texture");
- mPositionHandle = glGetAttribLocation(programId, "a_Position");
- mNormalHandle = glGetAttribLocation(programId, "a_Normal");
- mTexCoordHandle = glGetAttribLocation(programId, "a_TexCoordinate");
}
-void BasicProgram::before(Matrix& model, Matrix& view, Matrix& projection) {
+void PerspectiveProgram::before(Matrix& model, Matrix& view, Matrix& projection) {
Program::before(model, view, projection);
mLightModelMatrix.identity();
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/BasicProgram.h b/suite/pts/deviceTests/opengl/jni/graphics/PerspectiveProgram.h
similarity index 80%
rename from suite/pts/deviceTests/opengl/jni/graphics/BasicProgram.h
rename to suite/pts/deviceTests/opengl/jni/graphics/PerspectiveProgram.h
index d397719..7792a3d 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/BasicProgram.h
+++ b/suite/pts/deviceTests/opengl/jni/graphics/PerspectiveProgram.h
@@ -11,8 +11,8 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-#ifndef BASICPROGRAM_H
-#define BASICPROGRAM_H
+#ifndef PERSPECTIVEPROGRAM_H
+#define PERSPECTIVEPROGRAM_H
#include "Matrix.h"
#include "Program.h"
@@ -21,10 +21,12 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-class BasicProgram: public Program {
+class PerspectiveProgram: public Program {
public:
- BasicProgram(GLuint programId);
- virtual ~BasicProgram() {};
+ PerspectiveProgram(GLuint programId);
+ virtual ~PerspectiveProgram() {};
+ virtual void before(Matrix& model, Matrix& view, Matrix& projection);
+
Matrix mMVMatrix;
Matrix mMVPMatrix;
Matrix mLightModelMatrix;
@@ -35,11 +37,6 @@
int mMVMatrixHandle;
int mMVPMatrixHandle;
int mLightPosHandle;
- int mTexCoordHandle;
- int mPositionHandle;
- int mNormalHandle;
- int mTextureUniformHandle;
- virtual void before(Matrix& model, Matrix& view, Matrix& projection);
};
#endif
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/Program.h b/suite/pts/deviceTests/opengl/jni/graphics/Program.h
index f351b58..6161baf 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/Program.h
+++ b/suite/pts/deviceTests/opengl/jni/graphics/Program.h
@@ -24,7 +24,6 @@
virtual ~Program() {};
virtual void before(Matrix& model, Matrix& view, Matrix& projection);
virtual void after(Matrix& model, Matrix& view, Matrix& projection);
-private:
GLuint mProgramId;
};
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/ProgramNode.cpp b/suite/pts/deviceTests/opengl/jni/graphics/ProgramNode.cpp
index bc4eb90..f367268 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/ProgramNode.cpp
+++ b/suite/pts/deviceTests/opengl/jni/graphics/ProgramNode.cpp
@@ -14,12 +14,18 @@
#include "ProgramNode.h"
-void ProgramNode::before(Program& program, Matrix& model, Matrix& view,
- Matrix& projection) {
+ProgramNode::ProgramNode(Program& program) :
+ mProgram(program) {
+}
+
+void ProgramNode::before(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
program.before(model, view, projection);
}
-void ProgramNode::after(Program& program, Matrix& model, Matrix& view,
- Matrix& projection) {
+void ProgramNode::after(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
program.after(model, view, projection);
}
+
+void ProgramNode::drawProgram(Matrix& model, Matrix& view, Matrix& projection) {
+ SceneGraphNode::draw(mProgram, model, view, projection);
+}
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/ProgramNode.h b/suite/pts/deviceTests/opengl/jni/graphics/ProgramNode.h
index 54ac92a..fe69958 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/ProgramNode.h
+++ b/suite/pts/deviceTests/opengl/jni/graphics/ProgramNode.h
@@ -20,13 +20,13 @@
class ProgramNode: public SceneGraphNode {
public:
- ProgramNode() {};
+ ProgramNode(Program& program);
virtual ~ProgramNode() {};
+ void drawProgram(Matrix& model, Matrix& view, Matrix& projection);
protected:
- virtual void before(Program& program, Matrix& model, Matrix& view,
- Matrix& projection);
- virtual void after(Program& program, Matrix& model, Matrix& view,
- Matrix& projection);
+ virtual void before(Program& program, Matrix& model, Matrix& view, Matrix& projection);
+ virtual void after(Program& program, Matrix& model, Matrix& view, Matrix& projection);
+ Program& mProgram;
};
#endif
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/Renderer.cpp b/suite/pts/deviceTests/opengl/jni/graphics/Renderer.cpp
index e9d083d..e98296e 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/Renderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/graphics/Renderer.cpp
@@ -83,8 +83,6 @@
return false;
}
- glViewport(0, 0, mWidth, mHeight);
-
if (mOffscreen) {
mFboWidth = GLUtils::roundUpToSmallestPowerOf2(mWidth);
mFboHeight = GLUtils::roundUpToSmallestPowerOf2(mHeight);
@@ -92,18 +90,20 @@
return false;
}
mBuffer = new GLushort[mFboWidth * mFboHeight];
+ glViewport(0, 0, mFboWidth, mFboHeight);
} else {
mFboWidth = 0;
mFboHeight = 0;
- mBuffer = 0;
+ mBuffer = NULL;
mFboId = 0;
mRboId = 0;
mCboId = 0;
+ glViewport(0, 0, mWidth, mHeight);
}
GLuint err = glGetError();
if (err != GL_NO_ERROR) {
- ALOGE("GLError %d", err);
+ ALOGE("GLError %d in setUp", err);
return false;
}
return true;
@@ -111,8 +111,9 @@
bool Renderer::tearDown() {
SCOPED_TRACE();
- if (mBuffer != 0) {
+ if (mBuffer != NULL) {
delete[] mBuffer;
+ mBuffer = NULL;
}
if (mFboId != 0) {
glDeleteFramebuffers(1, &mFboId);
@@ -139,6 +140,13 @@
eglTerminate(mEglDisplay);
mEglDisplay = EGL_NO_DISPLAY;
}
+
+ GLuint err = glGetError();
+ if (err != GL_NO_ERROR) {
+ ALOGE("GLError %d in tearDown", err);
+ return false;
+ }
+
return EGL_SUCCESS == eglGetError();
}
@@ -146,7 +154,7 @@
SCOPED_TRACE();
GLuint err = glGetError();
if (err != GL_NO_ERROR) {
- ALOGE("GLError %d", err);
+ ALOGE("GLError %d in draw", err);
return false;
}
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/Renderer.h b/suite/pts/deviceTests/opengl/jni/graphics/Renderer.h
index 8da504d..6336f2e 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/Renderer.h
+++ b/suite/pts/deviceTests/opengl/jni/graphics/Renderer.h
@@ -38,11 +38,12 @@
GLuint mFboId;// Frame buffer id
GLuint mRboId;// Depth buffer id
GLuint mCboId;// Color buffer id
- GLushort* mBuffer;// Used for FBO read back
GLuint mProgramId;
EGLint mWidth;
EGLint mHeight;
bool mOffscreen;
int mWorkload;
+private:
+ GLushort* mBuffer;// Used for FBO read back
};
#endif
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/SceneGraphNode.cpp b/suite/pts/deviceTests/opengl/jni/graphics/SceneGraphNode.cpp
index 473c846..455ccd6 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/SceneGraphNode.cpp
+++ b/suite/pts/deviceTests/opengl/jni/graphics/SceneGraphNode.cpp
@@ -23,8 +23,7 @@
mChildren.add(child);
}
-void SceneGraphNode::draw(Program& program, Matrix& model, Matrix& view,
- Matrix& projection) {
+void SceneGraphNode::draw(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
before(program, model, view, projection);
for (size_t i = 0; i < mChildren.size(); i++) {
mChildren[i]->draw(program, model, view, projection);
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/SceneGraphNode.h b/suite/pts/deviceTests/opengl/jni/graphics/SceneGraphNode.h
index 55a20f1..dce8fde 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/SceneGraphNode.h
+++ b/suite/pts/deviceTests/opengl/jni/graphics/SceneGraphNode.h
@@ -23,13 +23,10 @@
SceneGraphNode() {};
virtual ~SceneGraphNode();
void addChild(SceneGraphNode* child);
- void draw(Program& program, Matrix& model, Matrix& view,
- Matrix& projection);
protected:
- virtual void before(Program& program, Matrix& model, Matrix& view,
- Matrix& projection) = 0;
- virtual void after(Program& program, Matrix& model, Matrix& view,
- Matrix& projection) = 0;
+ virtual void before(Program& program, Matrix& model, Matrix& view, Matrix& projection) = 0;
+ virtual void after(Program& program, Matrix& model, Matrix& view, Matrix& projection) = 0;
+ void draw(Program& program, Matrix& model, Matrix& view, Matrix& projection);
private:
android::Vector<SceneGraphNode*> mChildren;
};
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/TexturedMeshNode.cpp b/suite/pts/deviceTests/opengl/jni/graphics/TexturedMeshNode.cpp
new file mode 100644
index 0000000..17b50d7
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/graphics/TexturedMeshNode.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "TexturedMeshNode.h"
+
+TexturedMeshNode::TexturedMeshNode(const Mesh* mesh, const GLuint textureId) :
+ MeshNode(mesh), mTextureId(textureId) {
+}
+
+void TexturedMeshNode::before(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
+ int textureUniformHandle = glGetUniformLocation(program.mProgramId, "u_Texture");
+ int positionHandle = glGetAttribLocation(program.mProgramId, "a_Position");
+ int texCoordHandle = glGetAttribLocation(program.mProgramId, "a_TexCoordinate");
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+ glUniform1i(textureUniformHandle, 0);
+
+ glEnableVertexAttribArray(positionHandle);
+ glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, 0, mMesh->mVertices);
+ glEnableVertexAttribArray(texCoordHandle);
+ glVertexAttribPointer(texCoordHandle, 2, GL_FLOAT, false, 0, mMesh->mTexCoords);
+
+ glDrawArrays(GL_TRIANGLES, 0, mMesh->mNumVertices);
+}
+
+void TexturedMeshNode::after(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
+}
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.h b/suite/pts/deviceTests/opengl/jni/graphics/TexturedMeshNode.h
similarity index 77%
rename from suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.h
rename to suite/pts/deviceTests/opengl/jni/graphics/TexturedMeshNode.h
index 97cd24b..fff95aa 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.h
+++ b/suite/pts/deviceTests/opengl/jni/graphics/TexturedMeshNode.h
@@ -12,23 +12,21 @@
* the License.
*/
-#ifndef BASICMESHNODE_H
-#define BASICMESHNODE_H
+#ifndef TEXTUREDMESHNODE_H
+#define TEXTUREDMESHNODE_H
#include "Matrix.h"
#include "Mesh.h"
#include "MeshNode.h"
#include "Program.h"
-class BasicMeshNode: public MeshNode {
+class TexturedMeshNode: public MeshNode {
public:
- BasicMeshNode(const Mesh* mesh, const GLuint textureId);
- virtual ~BasicMeshNode() {};
+ TexturedMeshNode(const Mesh* mesh, const GLuint textureId);
+ virtual ~TexturedMeshNode() {};
protected:
- virtual void before(Program& program, Matrix& model, Matrix& view,
- Matrix& projection);
- virtual void after(Program& program, Matrix& model, Matrix& view,
- Matrix& projection);
+ virtual void before(Program& program, Matrix& model, Matrix& view, Matrix& projection);
+ virtual void after(Program& program, Matrix& model, Matrix& view, Matrix& projection);
const GLuint mTextureId;
};
diff --git a/suite/pts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp b/suite/pts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp
index 01b143b..f31cc99 100644
--- a/suite/pts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp
@@ -17,7 +17,7 @@
#include "FullPipelineRenderer.h"
-#include <graphics/BasicMeshNode.h>
+#include <graphics/PerspectiveMeshNode.h>
#include <graphics/GLUtils.h>
#include <graphics/TransformationNode.h>
@@ -107,7 +107,7 @@
if (mProgramId == 0) {
return false;
}
- mProgram = new BasicProgram(mProgramId);
+ mProgram = new PerspectiveProgram(mProgramId);
mModelMatrix = new Matrix();
@@ -152,7 +152,7 @@
float scale = 2.0f / count;
mMesh = new Mesh(FP_VERTICES, FP_NORMALS, FP_TEX_COORDS, FP_NUM_VERTICES);
- mSceneGraph = new ProgramNode();
+ mSceneGraph = new ProgramNode(*mProgram);
for (int i = 0; i < count; i++) {
for (int j = 0; j < count; j++) {
@@ -160,7 +160,7 @@
transformMatrix->translate(i - middle, j - middle, 0.0f);
TransformationNode* transformNode = new TransformationNode(transformMatrix);
mSceneGraph->addChild(transformNode);
- BasicMeshNode* meshNode = new BasicMeshNode(mMesh, mTextureId);
+ PerspectiveMeshNode* meshNode = new PerspectiveMeshNode(mMesh, mTextureId);
transformNode->addChild(meshNode);
}
}
@@ -193,6 +193,10 @@
bool FullPipelineRenderer::draw() {
SCOPED_TRACE();
+ if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)
+ || EGL_SUCCESS != eglGetError()) {
+ return false;
+ }
if (mOffscreen) {
glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
}
@@ -204,7 +208,7 @@
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
mModelMatrix->identity();
- mSceneGraph->draw(*mProgram, *mModelMatrix, *mViewMatrix, *mProjectionMatrix);
+ mSceneGraph->drawProgram(*mModelMatrix, *mViewMatrix, *mProjectionMatrix);
return Renderer::draw();
}
diff --git a/suite/pts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h b/suite/pts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h
index b0b31a2..235fc55 100644
--- a/suite/pts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h
+++ b/suite/pts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h
@@ -14,7 +14,7 @@
#ifndef FULLPIPELINERENDERER_H
#define FULLPIPELINERENDERER_H
-#include <graphics/BasicProgram.h>
+#include <graphics/PerspectiveProgram.h>
#include <graphics/Matrix.h>
#include <graphics/Mesh.h>
#include <graphics/Renderer.h>
@@ -28,7 +28,7 @@
bool tearDown();
bool draw();
private:
- BasicProgram* mProgram;
+ Program* mProgram;
ProgramNode* mSceneGraph;
Matrix* mModelMatrix;
Matrix* mViewMatrix;
diff --git a/suite/pts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp b/suite/pts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp
index 3fdafca..6e3a664 100644
--- a/suite/pts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp
@@ -92,6 +92,10 @@
bool PixelOutputRenderer::draw() {
SCOPED_TRACE();
+ if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)
+ || EGL_SUCCESS != eglGetError()) {
+ return false;
+ }
if (mOffscreen) {
glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
}
diff --git a/suite/pts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp b/suite/pts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp
index 85e6af3..05f4c65 100644
--- a/suite/pts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp
@@ -143,6 +143,10 @@
bool ShaderPerfRenderer::draw() {
SCOPED_TRACE();
+ if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)
+ || EGL_SUCCESS != eglGetError()) {
+ return false;
+ }
if (mOffscreen) {
glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
}
diff --git a/suite/pts/deviceTests/opengl/jni/reference/GLReference.cpp b/suite/pts/deviceTests/opengl/jni/reference/GLReference.cpp
index 333e0da..7bcca20 100644
--- a/suite/pts/deviceTests/opengl/jni/reference/GLReference.cpp
+++ b/suite/pts/deviceTests/opengl/jni/reference/GLReference.cpp
@@ -23,26 +23,28 @@
extern "C" JNIEXPORT jboolean JNICALL
Java_com_android_pts_opengl_reference_GLGameActivity_startBenchmark(
- JNIEnv* env, jclass clazz, jobject surface, jint numFrames,
+ JNIEnv* env, jclass clazz, jobject assetManager, jobject surface, jint numFrames,
jdoubleArray setUpTimes, jdoubleArray updateTimes, jdoubleArray renderTimes) {
+ GLUtils::setEnvAndAssetManager(env, assetManager);
+
if (numFrames > (ReferenceRenderer::FRAMES_PER_SCENE * ReferenceRenderer::NUM_SCENES)) {
return false;
}
- ReferenceRenderer* gRenderer = new ReferenceRenderer(ANativeWindow_fromSurface(env, surface));
+ ReferenceRenderer* renderer = new ReferenceRenderer(ANativeWindow_fromSurface(env, surface));
- bool success = gRenderer->setUp();
+ bool success = renderer->setUp();
env->SetDoubleArrayRegion(
- setUpTimes, 0, ReferenceRenderer::NUM_SETUP_TIMES, gRenderer->mSetUpTimes);
+ setUpTimes, 0, ReferenceRenderer::NUM_SETUP_TIMES, renderer->mSetUpTimes);
double updates[numFrames];
double renders[numFrames];
for (int i = 0; i < numFrames && success; i++) {
double t0 = GLUtils::currentTimeMillis();
- success = gRenderer->update(i);
+ success = renderer->update(i);
double t1 = GLUtils::currentTimeMillis();
- success = success && gRenderer->draw();
+ success = success && renderer->draw();
double t2 = GLUtils::currentTimeMillis();
updates[i] = t1 - t0;
renders[i] = t2 - t1;
@@ -51,8 +53,8 @@
env->SetDoubleArrayRegion(updateTimes, 0, numFrames, updates);
env->SetDoubleArrayRegion(renderTimes, 0, numFrames, renders);
- success = gRenderer->tearDown() && success;
- delete gRenderer;
- gRenderer = NULL;
+ success = renderer->tearDown() && success;
+ delete renderer;
+ renderer = NULL;
return success;
}
diff --git a/suite/pts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp b/suite/pts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp
index a168246..f4aef80 100644
--- a/suite/pts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp
@@ -14,6 +14,7 @@
#include "ReferenceRenderer.h"
#include "scene/flocking/FlockingScene.h"
+#include "scene/glowing/GlowingScene.h"
#include <graphics/GLUtils.h>
#include <graphics/ProgramNode.h>
@@ -39,6 +40,7 @@
// Create the scenes.
mScenes[0] = new FlockingScene(mWidth, mHeight);
+ mScenes[1] = new GlowingScene(mWidth, mHeight);
// TODO add more scenes to do a comprehensive test.
// Set up the scenes.
@@ -84,6 +86,10 @@
bool ReferenceRenderer::draw() {
SCOPED_TRACE();
+ if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)
+ || EGL_SUCCESS != eglGetError()) {
+ return false;
+ }
if (mOffscreen) {
glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
}
@@ -93,8 +99,9 @@
glEnable(GL_CULL_FACE);
// Use depth testing.
glEnable(GL_DEPTH_TEST);
- glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
- mCurrentScene->draw();
- return Renderer::draw();
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ bool success = mCurrentScene->draw();
+
+ return Renderer::draw() && success;
}
diff --git a/suite/pts/deviceTests/opengl/jni/reference/ReferenceRenderer.h b/suite/pts/deviceTests/opengl/jni/reference/ReferenceRenderer.h
index fa77ec1..21aa376 100644
--- a/suite/pts/deviceTests/opengl/jni/reference/ReferenceRenderer.h
+++ b/suite/pts/deviceTests/opengl/jni/reference/ReferenceRenderer.h
@@ -29,7 +29,7 @@
bool draw();
double mSetUpTimes[4];
static const int FRAMES_PER_SCENE = 500;
- static const int NUM_SCENES = 1;
+ static const int NUM_SCENES = 2;
static const int NUM_SETUP_TIMES = 4;
private:
Scene* mScenes[NUM_SCENES];
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/Scene.cpp b/suite/pts/deviceTests/opengl/jni/reference/scene/Scene.cpp
index 7ea889b..abf3a4e 100644
--- a/suite/pts/deviceTests/opengl/jni/reference/scene/Scene.cpp
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/Scene.cpp
@@ -19,13 +19,12 @@
#include <Trace.h>
Scene::Scene(int width, int height) :
- mWidth(width), mHeight(height), mSceneGraph(NULL) {
+ mWidth(width), mHeight(height) {
}
bool Scene::setUpContext() {
SCOPED_TRACE();
- mProgram = setUpProgram();
- if (mProgram == NULL) {
+ if (!setUpPrograms()) {
return false;
}
mModelMatrix = setUpModelMatrix();
@@ -36,7 +35,7 @@
if (mViewMatrix == NULL) {
return false;
}
- mProjectionMatrix = setUpProjectionMatrix();
+ mProjectionMatrix = setUpProjectionMatrix(mWidth, mHeight);
if (mProjectionMatrix == NULL) {
return false;
}
@@ -51,10 +50,9 @@
for (size_t i = 0; i < mMeshes.size(); i++) {
delete mMeshes[i];
}
- delete mProgram;
- mProgram = NULL;
- delete mSceneGraph;
- mSceneGraph = NULL;
+ for (size_t i = 0; i < mSceneGraphs.size(); i++) {
+ delete mSceneGraphs[i];
+ }
delete mModelMatrix;
mModelMatrix = NULL;
delete mViewMatrix;
@@ -66,16 +64,15 @@
bool Scene::update(int frame) {
SCOPED_TRACE();
- delete mSceneGraph; // Delete the old scene graph.
- mSceneGraph = updateSceneGraph();
- if (mSceneGraph == NULL) {
- return false;
+ // Delete the old scene graphs.
+ for (size_t i = 0; i < mSceneGraphs.size(); i++) {
+ delete mSceneGraphs[i];
}
- return true;
+ mSceneGraphs.clear();
+ return updateSceneGraphs(frame);
}
-bool Scene::draw() {
- SCOPED_TRACE();
- mSceneGraph->draw(*mProgram, *mModelMatrix, *mViewMatrix, *mProjectionMatrix);
- return true;
+void Scene::drawSceneGraph(int index) {
+ mModelMatrix->identity();
+ mSceneGraphs[index]->drawProgram(*mModelMatrix, *mViewMatrix, *mProjectionMatrix);
}
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/Scene.h b/suite/pts/deviceTests/opengl/jni/reference/scene/Scene.h
index 1adb850..06178b9 100644
--- a/suite/pts/deviceTests/opengl/jni/reference/scene/Scene.h
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/Scene.h
@@ -17,7 +17,7 @@
#include <graphics/Matrix.h>
#include <graphics/Mesh.h>
#include <graphics/Program.h>
-#include <graphics/SceneGraphNode.h>
+#include <graphics/ProgramNode.h>
#include <utils/Vector.h>
@@ -30,20 +30,20 @@
virtual bool setUpMeshes() = 0;
virtual bool tearDown();
virtual bool update(int frame);
- virtual bool draw();
+ virtual bool draw() = 0;
+ void drawSceneGraph(int index);
protected:
- virtual Program* setUpProgram() = 0;
+ virtual bool setUpPrograms() = 0;
virtual Matrix* setUpModelMatrix() = 0;
virtual Matrix* setUpViewMatrix() = 0;
- virtual Matrix* setUpProjectionMatrix() = 0;
- virtual SceneGraphNode* updateSceneGraph() = 0;
+ virtual Matrix* setUpProjectionMatrix(float width, float height) = 0;
+ virtual bool updateSceneGraphs(int frame) = 0;
int mWidth;
int mHeight;
android::Vector<Mesh*> mMeshes;
android::Vector<GLuint> mTextureIds;
+ android::Vector<ProgramNode*> mSceneGraphs;
private:
- Program* mProgram;
- SceneGraphNode* mSceneGraph;
Matrix* mModelMatrix;
Matrix* mViewMatrix;
Matrix* mProjectionMatrix;
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/Boid.h b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/Boid.h
index 955b891..e50acd0 100644
--- a/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/Boid.h
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/Boid.h
@@ -22,10 +22,13 @@
Boid(float x, float y);
void resetAcceleration();
void flock(const Boid* boids[], int numBoids, int index, float limitX, float limitY);
- static const float MAX_SPEED = 2.0f;
- static const float MAX_FORCE = 0.05f;
- static const float NEIGHBOUR_RADIUS = 70.0f;//50
- static const float DESIRED_BOID_DIST = 30.0f;//25
+ // The following floats are the parameters for the flocking algorithm, changing these
+ // modifies the boid's behaviour.
+ static const float MAX_SPEED = 2.0f;// Upper limit of boid velocity.
+ static const float MAX_FORCE = 0.05f;// Upper limit of the force used to push a boid.
+ static const float NEIGHBOUR_RADIUS = 70.0f;// Radius used to find neighbours, was 50.
+ static const float DESIRED_BOID_DIST = 35.0f;// Distance boids want to be from others, was 25.
+ // The weightings of the components.
static const float SEPARATION_WEIGHT = 2.0f;
static const float ALIGNMENT_WEIGHT = 1.0f;
static const float COHESION_WEIGHT = 1.0f;
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/FlockingScene.cpp b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/FlockingScene.cpp
index 933891a..71e5dac 100644
--- a/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/FlockingScene.cpp
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/FlockingScene.cpp
@@ -13,104 +13,59 @@
*/
#include "FlockingScene.h"
+#include "WaterMeshNode.h"
+
#include <cstdlib>
+#include <cmath>
#include <Trace.h>
-#include <graphics/BasicMeshNode.h>
-#include <graphics/BasicProgram.h>
+#include <graphics/PerspectiveMeshNode.h>
+#include <graphics/PerspectiveProgram.h>
#include <graphics/GLUtils.h>
#include <graphics/Matrix.h>
#include <graphics/Mesh.h>
#include <graphics/ProgramNode.h>
#include <graphics/TransformationNode.h>
-static const int FS_NUM_VERTICES = 6;
-
-static const float FS_VERTICES[FS_NUM_VERTICES * 3] = {
- 1.0f, 1.0f, 0.0f,
- -1.0f, 1.0f, 0.0f,
- -1.0f, -1.0f, 0.0f,
- -1.0f, -1.0f, 0.0f,
- 1.0f, -1.0f, 0.0f,
- 1.0f, 1.0f, 0.0f };
-
-static const float FS_NORMALS[FS_NUM_VERTICES * 3] = {
- 0.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f };
-
-static const float FS_TEX_COORDS[FS_NUM_VERTICES * 2] = {
- 1.0f, 1.0f,
- 0.0f, 1.0f,
- 0.0f, 0.0f,
- 0.0f, 0.0f,
- 1.0f, 0.0f,
- 1.0f, 1.0f };
-
-static const char* FS_VERTEX =
- "uniform mat4 u_MVPMatrix;"
- "uniform mat4 u_MVMatrix;"
- "attribute vec4 a_Position;"
- "attribute vec3 a_Normal;"
- "attribute vec2 a_TexCoordinate;"
- "varying vec3 v_Position;"
- "varying vec3 v_Normal;"
- "varying vec2 v_TexCoordinate;"
- "void main() {\n"
- " // Transform the vertex into eye space.\n"
- " v_Position = vec3(u_MVMatrix * a_Position);\n"
- " // Pass through the texture coordinate.\n"
- " v_TexCoordinate = a_TexCoordinate;\n"
- " // Transform the normal\'s orientation into eye space.\n"
- " v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));\n"
- " // Multiply to get the final point in normalized screen coordinates.\n"
- " gl_Position = u_MVPMatrix * a_Position;\n"
- "}";
-
-static const char* FS_FRAGMENT =
- "precision mediump float;"
- "uniform vec3 u_LightPos;"
- "uniform sampler2D u_Texture;"
- "varying vec3 v_Position;"
- "varying vec3 v_Normal;"
- "varying vec2 v_TexCoordinate;"
- "void main() {\n"
- " // Will be used for attenuation.\n"
- " float distance = length(u_LightPos - v_Position);\n"
- " // Get a lighting direction vector from the light to the vertex.\n"
- " vec3 lightVector = normalize(u_LightPos - v_Position);\n"
- " // Calculate the dot product of the light vector and vertex normal.\n"
- " float diffuse = max(dot(v_Normal, lightVector), 0.0);\n"
- " // Add attenuation.\n"
- " diffuse = diffuse * (1.0 / (1.0 + (0.01 * distance)));\n"
- " // Add ambient lighting\n"
- " diffuse = diffuse + 0.25;\n"
- " // Multiply the diffuse illumination and texture to get final output color.\n"
- " gl_FragColor = (diffuse * texture2D(u_Texture, v_TexCoordinate));\n"
- "}";
-
FlockingScene::FlockingScene(int width, int height) :
- Scene(width, height) {
+ Scene(width, height), mMainProgram(NULL), mWaterProgram(NULL) {
for (int i = 0; i < NUM_BOIDS; i++) {
- // Generate a boid with a random position.
- float x = ((rand() % 10) / 5.0f) - 0.1f;
- float y = ((rand() % 10) / 5.0f) - 0.1f;
+ // Generate a boid with a random position. (-50, 50)
+ float x = (rand() % 101) - 50.0f;
+ float y = (rand() % 101) - 50.0f;
mBoids[i] = new Boid(x, y);
}
}
-Program* FlockingScene::setUpProgram() {
- // TODO Enable loading programs from file.
- // mProgramId = GLUtils::loadProgram("flocking");
- GLuint programId = GLUtils::createProgram(&FS_VERTEX, &FS_FRAGMENT);
- if (programId == 0) {
- return NULL;
+bool FlockingScene::setUpPrograms() {
+ // Main Program
+ const char* vertex = GLUtils::openTextFile("vertex/perspective");
+ const char* fragment = GLUtils::openTextFile("fragment/perspective");
+ if (vertex == NULL || fragment == NULL) {
+ return false;
}
- return new BasicProgram(programId);
+ GLuint programId = GLUtils::createProgram(&vertex, &fragment);
+ delete[] vertex;
+ delete[] fragment;
+ if (programId == 0) {
+ return false;
+ }
+ mMainProgram = new PerspectiveProgram(programId);
+ // Water Program
+ vertex = GLUtils::openTextFile("vertex/water");
+ fragment = GLUtils::openTextFile("fragment/water");
+ if (vertex == NULL || fragment == NULL) {
+ return false;
+ }
+ programId = GLUtils::createProgram(&vertex, &fragment);
+ delete[] vertex;
+ delete[] fragment;
+ if (programId == 0) {
+ return false;
+ }
+ mWaterProgram = new PerspectiveProgram(programId);
+ return true;
}
Matrix* FlockingScene::setUpModelMatrix() {
@@ -121,7 +76,7 @@
// Position the eye in front of the origin.
float eyeX = 0.0f;
float eyeY = 0.0f;
- float eyeZ = 2.0f;
+ float eyeZ = 10.0f;
// We are looking at the origin
float centerX = 0.0f;
@@ -137,37 +92,36 @@
return Matrix::newLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
}
-Matrix* FlockingScene::setUpProjectionMatrix() {
+Matrix* FlockingScene::setUpProjectionMatrix(float width, float height) {
// Create a new perspective projection matrix. The height will stay the same
// while the width will vary as per aspect ratio.
- mDisplayRatio = ((float) mWidth) / ((float) mHeight);
+ mDisplayRatio = width / height;
+ // Set board dimensions
mBoardHeight = 1000.0f;
mBoardWidth = mDisplayRatio * mBoardHeight;
float left = -mDisplayRatio;
float right = mDisplayRatio;
float bottom = -1.0f;
float top = 1.0f;
- float near = 1.0f;
- float far = 3.0f;
- // Set board dimensions
+ float near = 8.0f;
+ float far = 12.0f;
return Matrix::newFrustum(left, right, bottom, top, near, far);
}
bool FlockingScene::setUpTextures() {
SCOPED_TRACE();
- mTextureIds.add(GLUtils::genTexture(256, 256, GLUtils::RANDOM_FILL));
- mTextureIds.add(GLUtils::genTexture(1, 1, 0xc0c0c0));
- // TODO Enable loading textures from file.
- // mTextureIds.add(GLUtils::loadTexture("knight.jpg"));
+ mTextureIds.add(GLUtils::loadTexture("texture/fish_dark.png"));
+ mTextureIds.add(GLUtils::loadTexture("texture/background.png"));
+ mTextureIds.add(GLUtils::loadTexture("texture/water1.png"));
+ mTextureIds.add(GLUtils::loadTexture("texture/water2.png"));
return true;
}
bool FlockingScene::setUpMeshes() {
SCOPED_TRACE();
- mMeshes.add(new Mesh(FS_VERTICES, FS_NORMALS, FS_TEX_COORDS, FS_NUM_VERTICES));
- // TODO Enable loading meshes from file.
- // mMeshes.add(GLUtils::loadMesh("knight.obj", mTextureIds[0]));
+ mMeshes.add(GLUtils::loadMesh("mesh/fish.cob"));
+ mMeshes.add(GLUtils::loadMesh("mesh/plane.cob"));
return true;
}
@@ -176,22 +130,29 @@
for (int i = 0; i < NUM_BOIDS; i++) {
delete mBoids[i];
}
+ delete mMainProgram;
+ delete mWaterProgram;
return Scene::tearDown();
}
-SceneGraphNode* FlockingScene::updateSceneGraph() {
- const float MAIN_SCALE = 2.0f; // Scale up as the camera is far away.
+bool FlockingScene::updateSceneGraphs(int frame) {
+ const float MAIN_SCALE = 1.25f; // Scale up as the camera is far away.
const float LIMIT_X = mBoardWidth / 2.0f;
const float LIMIT_Y = mBoardHeight / 2.0f;
- SceneGraphNode* sceneGraph = new ProgramNode();
- Matrix* transformMatrix = Matrix::newScale(MAIN_SCALE * mDisplayRatio, MAIN_SCALE, MAIN_SCALE);
+
+ ProgramNode* mainSceneGraph = new ProgramNode(*mMainProgram);
+ mSceneGraphs.add(mainSceneGraph);
+ // Bottom
+ Matrix* transformMatrix = Matrix::newScale(MAIN_SCALE * mDisplayRatio, MAIN_SCALE, 0.0f);
TransformationNode* transformNode = new TransformationNode(transformMatrix);
- sceneGraph->addChild(transformNode);
- BasicMeshNode* meshNode = new BasicMeshNode(mMeshes[0], mTextureIds[1]);
+ mainSceneGraph->addChild(transformNode);
+ MeshNode* meshNode = new PerspectiveMeshNode(mMeshes[1], mTextureIds[1]);
transformNode->addChild(meshNode);
+ // Boids
+ const float MARGIN = 30.0f; // So the fish dont disappear and appear at the edges.
for (int i = 0; i < NUM_BOIDS; i++) {
Boid* b = mBoids[i];
- b->flock((const Boid**) &mBoids, NUM_BOIDS, i, LIMIT_X, LIMIT_Y);
+ b->flock((const Boid**) &mBoids, NUM_BOIDS, i, LIMIT_X + MARGIN, LIMIT_Y + MARGIN);
Vector2D* pos = &(b->mPosition);
Vector2D* vel = &(b->mVelocity);
@@ -199,13 +160,34 @@
float x = pos->mX / (LIMIT_X * BOID_SCALE) * mDisplayRatio;
float y = pos->mY / (LIMIT_Y * BOID_SCALE);
- // TODO need to include rotation.
- transformMatrix = Matrix::newScale(BOID_SCALE * MAIN_SCALE, BOID_SCALE * MAIN_SCALE, 1.0f);
- transformMatrix->translate(x, y, 0.01f);
+ const float SCALE = BOID_SCALE * MAIN_SCALE;
+ transformMatrix = Matrix::newScale(SCALE, SCALE, SCALE);
+ transformMatrix->translate(x, y, 1.0f);
+ transformMatrix->rotate(atan2(vel->mY, vel->mX) + M_PI, 0, 0, 1);
transformNode = new TransformationNode(transformMatrix);
- sceneGraph->addChild(transformNode);
- meshNode = new BasicMeshNode(mMeshes[0], mTextureIds[0]);
+ mainSceneGraph->addChild(transformNode);
+ meshNode = new PerspectiveMeshNode(mMeshes[0], mTextureIds[0]);
transformNode->addChild(meshNode);
}
- return sceneGraph;
+ ProgramNode* waterSceneGraph = new ProgramNode(*mWaterProgram);
+ mSceneGraphs.add(waterSceneGraph);
+ // Top
+ transformMatrix = Matrix::newScale(MAIN_SCALE * mDisplayRatio, MAIN_SCALE, 1.0f);
+ transformMatrix->translate(0, 0, 0.1f);
+ transformNode = new TransformationNode(transformMatrix);
+ waterSceneGraph->addChild(transformNode);
+ meshNode = new WaterMeshNode(mMeshes[1], frame, mTextureIds[2], mTextureIds[3]);
+ transformNode->addChild(meshNode);
+ return true;
+}
+
+bool FlockingScene::draw() {
+ SCOPED_TRACE();
+ drawSceneGraph(0); // Draw fish and pond bottom
+ // Use blending.
+ glEnable (GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ drawSceneGraph(1); // Draw water
+ glDisable(GL_BLEND);
+ return true;
}
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/FlockingScene.h b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/FlockingScene.h
index 6433f94..97bd4cc 100644
--- a/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/FlockingScene.h
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/FlockingScene.h
@@ -14,6 +14,8 @@
#ifndef FLOCKINGSCENE_H
#define FLOCKINGSCENE_H
+#include <graphics/Program.h>
+
#include "../Scene.h"
#include "Boid.h"
@@ -24,18 +26,21 @@
bool setUpTextures();
bool setUpMeshes();
bool tearDown();
+ bool draw();
static const int NUM_BOIDS = 100;
protected:
- Program* setUpProgram();
+ bool setUpPrograms();
Matrix* setUpModelMatrix();
Matrix* setUpViewMatrix();
- Matrix* setUpProjectionMatrix();
- SceneGraphNode* updateSceneGraph();
+ Matrix* setUpProjectionMatrix(float width, float height);
+ bool updateSceneGraphs(int frame);
private:
Boid* mBoids[NUM_BOIDS];
float mDisplayRatio;
float mBoardWidth;
float mBoardHeight;
+ Program* mMainProgram;
+ Program* mWaterProgram;
static const float BOID_SCALE = 1.0f / 50.0f;
};
#endif
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/WaterMeshNode.cpp b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/WaterMeshNode.cpp
new file mode 100644
index 0000000..f000d58
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/WaterMeshNode.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "WaterMeshNode.h"
+
+#include <graphics/PerspectiveProgram.h>
+
+WaterMeshNode::WaterMeshNode(const Mesh* mesh, int time, GLuint textureId1, GLuint textureId2) :
+ MeshNode(mesh), mTime(time), mTextureId1(textureId1), mTextureId2(textureId2) {
+}
+
+void WaterMeshNode::before(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
+ PerspectiveProgram& prog = (PerspectiveProgram&) program;
+
+ int textureUniformHandle1 = glGetUniformLocation(prog.mProgramId, "u_Texture1");
+ int textureUniformHandle2 = glGetUniformLocation(prog.mProgramId, "u_Texture2");
+ int timeUniformHandle = glGetUniformLocation(prog.mProgramId, "u_Time");
+ int positionHandle = glGetAttribLocation(prog.mProgramId, "a_Position");
+ int texCoordHandle = glGetAttribLocation(prog.mProgramId, "a_TexCoordinate");
+
+ glActiveTexture (GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mTextureId1);
+ glUniform1i(textureUniformHandle1, 0);
+
+ glActiveTexture (GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mTextureId2);
+ glUniform1i(textureUniformHandle2, 1);
+
+ glUniform1i(timeUniformHandle, mTime);
+
+ glEnableVertexAttribArray(positionHandle);
+ glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, 0, mMesh->mVertices);
+ glEnableVertexAttribArray(texCoordHandle);
+ glVertexAttribPointer(texCoordHandle, 2, GL_FLOAT, false, 0, mMesh->mTexCoords);
+
+ // This multiplies the view matrix by the model matrix, and stores the result in the MVP
+ // matrix (which currently contains model * view).
+ prog.mMVMatrix.multiply(view, model);
+
+ // Pass in the modelview matrix.
+ glUniformMatrix4fv(prog.mMVMatrixHandle, 1, false, prog.mMVMatrix.mData);
+
+ // This multiplies the modelview matrix by the projection matrix, and stores the result in
+ // the MVP matrix (which now contains model * view * projection).
+ prog.mMVPMatrix.multiply(projection, prog.mMVMatrix);
+
+ // Pass in the combined matrix.
+ glUniformMatrix4fv(prog.mMVPMatrixHandle, 1, false, prog.mMVPMatrix.mData);
+
+ // Pass in the light position in eye space.
+ glUniform3f(prog.mLightPosHandle, prog.mLightPosInEyeSpace[0], prog.mLightPosInEyeSpace[1],
+ prog.mLightPosInEyeSpace[2]);
+
+ glDrawArrays(GL_TRIANGLES, 0, mMesh->mNumVertices);
+}
+
+void WaterMeshNode::after(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
+}
diff --git a/suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.h b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/WaterMeshNode.h
similarity index 62%
copy from suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.h
copy to suite/pts/deviceTests/opengl/jni/reference/scene/flocking/WaterMeshNode.h
index 97cd24b..81bfe8f 100644
--- a/suite/pts/deviceTests/opengl/jni/graphics/BasicMeshNode.h
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/flocking/WaterMeshNode.h
@@ -12,24 +12,24 @@
* the License.
*/
-#ifndef BASICMESHNODE_H
-#define BASICMESHNODE_H
+#ifndef WATERMESHNODE_H
+#define WATERMESHNODE_H
-#include "Matrix.h"
-#include "Mesh.h"
-#include "MeshNode.h"
-#include "Program.h"
+#include <graphics/Matrix.h>
+#include <graphics/Mesh.h>
+#include <graphics/MeshNode.h>
+#include <graphics/Program.h>
-class BasicMeshNode: public MeshNode {
+class WaterMeshNode: public MeshNode {
public:
- BasicMeshNode(const Mesh* mesh, const GLuint textureId);
- virtual ~BasicMeshNode() {};
+ WaterMeshNode(const Mesh* mesh, int time, GLuint textureId1, GLuint textureId2);
+ virtual ~WaterMeshNode() {};
protected:
- virtual void before(Program& program, Matrix& model, Matrix& view,
- Matrix& projection);
- virtual void after(Program& program, Matrix& model, Matrix& view,
- Matrix& projection);
- const GLuint mTextureId;
+ virtual void before(Program& program, Matrix& model, Matrix& view, Matrix& projection);
+ virtual void after(Program& program, Matrix& model, Matrix& view, Matrix& projection);
+ const int mTime;
+ const GLuint mTextureId1;
+ const GLuint mTextureId2;
};
#endif
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/BlurMeshNode.cpp b/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/BlurMeshNode.cpp
new file mode 100644
index 0000000..6c2ad64
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/BlurMeshNode.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "BlurMeshNode.h"
+
+BlurMeshNode::BlurMeshNode(const Mesh* mesh, GLuint fboTexId, GLuint tmpTexId1, GLuint tmpTexId2,
+ float width, float height) :
+ MeshNode(mesh), mFboTexId(fboTexId), mTmpTexId1(tmpTexId1), mTmpTexId2(tmpTexId2),
+ mWidth(width), mHeight(height) {
+}
+
+void BlurMeshNode::before(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
+ int textureUniformHandle = glGetUniformLocation(program.mProgramId, "u_Texture");
+ int scaleUniformHandle = glGetUniformLocation(program.mProgramId, "u_Scale");
+ int positionHandle = glGetAttribLocation(program.mProgramId, "a_Position");
+ int texCoordHandle = glGetAttribLocation(program.mProgramId, "a_TexCoordinate");
+
+ glEnableVertexAttribArray(positionHandle);
+ glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, 0, mMesh->mVertices);
+ glEnableVertexAttribArray(texCoordHandle);
+ glVertexAttribPointer(texCoordHandle, 2, GL_FLOAT, false, 0, mMesh->mTexCoords);
+
+ // Blur X
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTmpTexId1, 0);
+ glActiveTexture (GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mFboTexId);
+ glUniform1i(textureUniformHandle, 0);
+
+ glUniform2f(scaleUniformHandle, 1.0f / mWidth, 0);
+
+ glDrawArrays(GL_TRIANGLES, 0, mMesh->mNumVertices);
+
+ // Blur Y
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTmpTexId2, 0);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mTmpTexId1);
+ glUniform1i(textureUniformHandle, 0);
+
+ glUniform2f(scaleUniformHandle, 0, 1.0f / mHeight);
+
+ glDrawArrays(GL_TRIANGLES, 0, mMesh->mNumVertices);
+}
+
+void BlurMeshNode::after(Program& program, Matrix& model, Matrix& view, Matrix& projection) {
+}
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/BlurMeshNode.h b/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/BlurMeshNode.h
new file mode 100644
index 0000000..8cb9617
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/BlurMeshNode.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef BLURMESHNODE_H
+#define BLURMESHNODE_H
+
+#include <graphics/Matrix.h>
+#include <graphics/Mesh.h>
+#include <graphics/MeshNode.h>
+#include <graphics/Program.h>
+
+class BlurMeshNode: public MeshNode {
+public:
+ BlurMeshNode(const Mesh* mesh, GLuint fboTexId, GLuint tmpTexId1, GLuint tmpTexId2, float width, float height);
+ virtual ~BlurMeshNode() {};
+protected:
+ virtual void before(Program& program, Matrix& model, Matrix& view, Matrix& projection);
+ virtual void after(Program& program, Matrix& model, Matrix& view, Matrix& projection);
+ const GLuint mFboTexId;
+ const GLuint mTmpTexId1;
+ const GLuint mTmpTexId2;
+ const float mWidth;
+ const float mHeight;
+};
+
+#endif
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/GlowingScene.cpp b/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/GlowingScene.cpp
new file mode 100644
index 0000000..0d22b96
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/GlowingScene.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#include "GlowingScene.h"
+#include "BlurMeshNode.h"
+
+#include <Trace.h>
+
+#include <graphics/PerspectiveMeshNode.h>
+#include <graphics/PerspectiveProgram.h>
+#include <graphics/Program.h>
+#include <graphics/GLUtils.h>
+#include <graphics/Mesh.h>
+#include <graphics/ProgramNode.h>
+#include <graphics/TransformationNode.h>
+
+GlowingScene::GlowingScene(int width, int height) :
+ Scene(width, height), mFboId(0), mMainProgram(NULL), mBlurProgram(NULL) {
+ mFboWidth = GLUtils::roundUpToSmallestPowerOf2(width);
+ mFboHeight = GLUtils::roundUpToSmallestPowerOf2(height);
+ mFboRatio = mFboWidth / mFboHeight;
+ mFboModelMatrix = setUpModelMatrix();
+ mFboViewMatrix = setUpViewMatrix();
+ mFboProjectionMatrix = setUpProjectionMatrix(mFboWidth, mFboHeight);
+}
+
+bool GlowingScene::setUpContext() {
+ if (!Scene::setUpContext()) {
+ return false;
+ }
+ // Create a fbo
+ glGenFramebuffers(1, &mFboId);
+ return true;
+}
+
+bool GlowingScene::setUpPrograms() {
+ // Main Program
+ const char* vertex = GLUtils::openTextFile("vertex/perspective");
+ const char* fragment = GLUtils::openTextFile("fragment/perspective");
+ if (vertex == NULL || fragment == NULL) {
+ return false;
+ }
+ GLuint programId = GLUtils::createProgram(&vertex, &fragment);
+ delete[] vertex;
+ delete[] fragment;
+ if (programId == 0) {
+ return false;
+ }
+ mMainProgram = new PerspectiveProgram(programId);
+ // Blur Program
+ vertex = GLUtils::openTextFile("vertex/blur");
+ fragment = GLUtils::openTextFile("fragment/blur");
+ if (vertex == NULL || fragment == NULL) {
+ return false;
+ }
+ programId = GLUtils::createProgram(&vertex, &fragment);
+ delete[] vertex;
+ delete[] fragment;
+ if (programId == 0) {
+ return false;
+ }
+ mBlurProgram = new Program(programId);
+ return true;
+}
+
+Matrix* GlowingScene::setUpModelMatrix() {
+ return new Matrix();
+}
+
+Matrix* GlowingScene::setUpViewMatrix() {
+ // Position the eye in front of the origin.
+ float eyeX = 0.0f;
+ float eyeY = 0.0f;
+ float eyeZ = 10.0f;
+
+ // Look at the origin
+ float centerX = 0.0f;
+ float centerY = 0.0f;
+ float centerZ = 0.0f;
+
+ // Set up vector.
+ float upX = 0.0f;
+ float upY = 1.0f;
+ float upZ = 0.0f;
+
+ // Set the view matrix.
+ return Matrix::newLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
+}
+
+Matrix* GlowingScene::setUpProjectionMatrix(float width, float height) {
+ // Create a new perspective projection matrix. The height will stay the same
+ // while the width will vary as per aspect ratio.
+ float ratio = width / height;
+ float left = -ratio;
+ float right = ratio;
+ float bottom = -1.0f;
+ float top = 1.0f;
+ float near = 8.0f;
+ float far = 12.0f;
+
+ return Matrix::newFrustum(left, right, bottom, top, near, far);
+}
+
+bool GlowingScene::setUpTextures() {
+ SCOPED_TRACE();
+ mTextureIds.add(GLUtils::genTexture(mWidth, mHeight, 0)); // fbo
+ mTextureIds.add(GLUtils::genTexture(mWidth, mHeight, 0)); // tmp1
+ mTextureIds.add(GLUtils::genTexture(mWidth, mHeight, 0)); // tmp2
+ mTextureIds.add(GLUtils::loadTexture("texture/arc.png"));
+ return true;
+}
+
+bool GlowingScene::setUpMeshes() {
+ SCOPED_TRACE();
+ mMeshes.add(GLUtils::loadMesh("mesh/plane.cob"));
+ mMeshes.add(GLUtils::loadMesh("mesh/arc.cob"));
+ return true;
+}
+
+bool GlowingScene::tearDown() {
+ SCOPED_TRACE();
+ if (mMainProgram != NULL) {
+ delete mMainProgram;
+ mMainProgram = NULL;
+ }
+ if (mBlurProgram != NULL) {
+ delete mBlurProgram;
+ mBlurProgram = NULL;
+ }
+ if (mFboId != 0) {
+ glDeleteFramebuffers(1, &mFboId);
+ mFboId = 0;
+ }
+ delete mFboModelMatrix;
+ mFboModelMatrix = NULL;
+ delete mFboViewMatrix;
+ mFboViewMatrix = NULL;
+ delete mFboProjectionMatrix;
+ mFboProjectionMatrix = NULL;
+ return Scene::tearDown();
+}
+
+bool GlowingScene::updateSceneGraphs(int frame) {
+ // To render the mesh to the FBO
+ ProgramNode* lightSceneGraph = new ProgramNode(*mMainProgram);
+ mSceneGraphs.add(lightSceneGraph);
+ MeshNode* meshNode = new PerspectiveMeshNode(mMeshes[1], mTextureIds[3]);
+ lightSceneGraph->addChild(meshNode);
+
+ // To blur the image
+ ProgramNode* blurSceneGraph = new ProgramNode(*mBlurProgram);
+ mSceneGraphs.add(blurSceneGraph);
+ meshNode = new BlurMeshNode(mMeshes[0], mTextureIds[0], mTextureIds[1], mTextureIds[2],
+ mFboWidth, mFboHeight);
+ blurSceneGraph->addChild(meshNode);
+
+ // Blur To screen
+ ProgramNode* glowSceneGraph = new ProgramNode(*mMainProgram);
+ mSceneGraphs.add(glowSceneGraph);
+ Matrix* transformMatrix = Matrix::newScale(mFboRatio, 1.0f, 1.0f);
+ TransformationNode* transformNode = new TransformationNode(transformMatrix);
+ glowSceneGraph->addChild(transformNode);
+ meshNode = new PerspectiveMeshNode(mMeshes[0], mTextureIds[2]);
+ transformNode->addChild(meshNode);
+ return true;
+}
+
+bool GlowingScene::draw() {
+ SCOPED_TRACE();
+ glBindFramebuffer(GL_FRAMEBUFFER, mFboId); // Use FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureIds[0], 0);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ return false;
+ }
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear (GL_COLOR_BUFFER_BIT);
+ glViewport(0, 0, mFboWidth, mFboHeight);
+ mFboModelMatrix->identity();
+ mSceneGraphs[0]->drawProgram(*mFboModelMatrix, *mFboViewMatrix, *mFboProjectionMatrix); // Mesh
+ mFboModelMatrix->identity();
+ mSceneGraphs[1]->drawProgram(*mFboModelMatrix, *mFboViewMatrix, *mFboProjectionMatrix); // Blur
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0); // Use Screen
+ glViewport(0, 0, mWidth, mHeight);
+ Scene::drawSceneGraph(2); // Blur to Screen
+ return true;
+}
diff --git a/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/GlowingScene.h b/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/GlowingScene.h
new file mode 100644
index 0000000..3bc734c
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/reference/scene/glowing/GlowingScene.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#ifndef GLOWINGSCENE_H
+#define GLOWINGSCENE_H
+
+#include <graphics/Program.h>
+
+#include "../Scene.h"
+
+class GlowingScene: public Scene {
+public:
+ GlowingScene(int width, int height);
+ virtual ~GlowingScene() {};
+ bool setUpContext();
+ bool setUpTextures();
+ bool setUpMeshes();
+ bool tearDown();
+ bool draw();
+protected:
+ bool setUpPrograms();
+ Matrix* setUpModelMatrix();
+ Matrix* setUpViewMatrix();
+ Matrix* setUpProjectionMatrix(float width, float height);
+ bool updateSceneGraphs(int frame);
+private:
+ GLuint mFboId;
+ Program* mMainProgram;
+ Program* mBlurProgram;
+ float mFboWidth;
+ float mFboHeight;
+ float mFboRatio;
+ Matrix* mFboModelMatrix;
+ Matrix* mFboViewMatrix;
+ Matrix* mFboProjectionMatrix;
+};
+#endif
diff --git a/suite/pts/deviceTests/opengl/res/layout/main.xml b/suite/pts/deviceTests/opengl/res/layout/main.xml
deleted file mode 100644
index 5f54a8c..0000000
--- a/suite/pts/deviceTests/opengl/res/layout/main.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2008 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <!-- TODO: Make this quite a heavy UI to load, use ImageViews etc -->
-
-</LinearLayout>
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLGameActivity.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLGameActivity.java
index 6cb0700..5fc1c1b 100644
--- a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLGameActivity.java
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLGameActivity.java
@@ -17,7 +17,12 @@
import android.app.Activity;
import android.content.Intent;
+import android.content.res.AssetManager;
import android.cts.util.WatchDog;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -25,6 +30,8 @@
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
@@ -82,8 +89,12 @@
worker.start();
}
- private static native boolean startBenchmark(Surface surface, int numFrames,
- double[] setUpTimes, double[] updateTimes, double[] renderTimes);
+ private static native boolean startBenchmark(AssetManager manager,
+ Surface surface,
+ int numFrames,
+ double[] setUpTimes,
+ double[] updateTimes,
+ double[] renderTimes);
/**
* This thread renderers the benchmarks, freeing the UI thread.
@@ -115,8 +126,12 @@
mUpdateTimes = new double[mNumFrames];
// Used to record the times taken to render.
mRenderTimes = new double[mNumFrames];
- boolean success =
- startBenchmark(mSurface, mNumFrames, mSetUpTimes, mUpdateTimes, mRenderTimes);
+ boolean success = startBenchmark(getAssets(),
+ mSurface,
+ mNumFrames,
+ mSetUpTimes,
+ mUpdateTimes,
+ mRenderTimes);
watchDog.stop();
mHandler.sendEmptyMessage((success) ? 1 : 0);
@@ -127,4 +142,41 @@
}
}
+
+ public static int loadTexture(AssetManager manager, String path) {
+ InputStream in = null;
+ try {
+ in = manager.open(path);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return -1;
+ }
+ BitmapFactory.Options op = new BitmapFactory.Options();
+ op.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ Bitmap bmp = BitmapFactory.decodeStream(in, null, op);
+ // generate textureID
+ int[] textures = new int[1];
+ GLES20.glGenTextures(1, textures, 0);
+ int textureID = textures[0];
+
+ // create texture
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID);
+ GLES20.glTexParameteri(
+ GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(
+ GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
+ GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
+
+ // clean up
+ try {
+ in.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ bmp.recycle();
+ }
+ return textureID;
+ }
}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLReferenceActivity.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLReferenceActivity.java
index 145fb75..c7fa7b9 100644
--- a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLReferenceActivity.java
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLReferenceActivity.java
@@ -14,7 +14,6 @@
package com.android.pts.opengl.reference;
import com.android.pts.opengl.GLActivityIntentKeys;
-import com.android.pts.opengl.R;
import android.app.Activity;
import android.content.Intent;
@@ -31,7 +30,6 @@
private int mTimeout;
public boolean mSuccess = false;
- public double mUILoadTime;
public double[] mSetUpTimes;
public double[] mUpdateTimes;
public double[] mRenderTimes;
@@ -45,10 +43,6 @@
mNumFrames = intent.getIntExtra(GLActivityIntentKeys.INTENT_EXTRA_NUM_FRAMES, 0);
mTimeout = intent.getIntExtra(GLActivityIntentKeys.INTENT_EXTRA_TIMEOUT, 0);
- double start = System.currentTimeMillis();
- setContentView(R.layout.main);
- mUILoadTime = System.currentTimeMillis() - start;
-
// Start benchmark
intent = new Intent(this, GLGameActivity.class);
intent.putExtra(GLActivityIntentKeys.INTENT_EXTRA_NUM_FRAMES, mNumFrames);
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLReferenceBenchmark.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLReferenceBenchmark.java
index 9c56e8e..6aebab8 100644
--- a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLReferenceBenchmark.java
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/reference/GLReferenceBenchmark.java
@@ -32,15 +32,9 @@
public class GLReferenceBenchmark extends PtsActivityInstrumentationTestCase2<GLReferenceActivity> {
private static final int NUM_FRAMES_PER_SCENE = 500;
- private static final int NUM_SCENES = 1;
+ private static final int NUM_SCENES = 2;
private static final int NUM_FRAMES = NUM_FRAMES_PER_SCENE * NUM_SCENES;
private static final int TIMEOUT = 1000000;
- // Reference values collected by averaging across n4, n7, n10.
- private static final double NEXUS_REF_UI_LOAD = 39.67f;// Milliseconds.
- // (GL, Context, Textures, Meshes).
- private static final double[] NEXUS_REF_SET_UP = {22.58f, 44.49f, 2.47f, 0.02f};// MS.
- private static final double NEXUS_REF_UPDATE_AVG = 9.8f;// MS.
- private static final double NEXUS_REF_RENDER_AVG = 0.44f;// Fraction of display refresh rate.
public GLReferenceBenchmark() {
super(GLReferenceActivity.class);
@@ -62,84 +56,45 @@
activity.waitForCompletion();
} finally {
if (activity != null) {
- double score = 0;
+ double totalTime = 0;
if (activity.mSuccess) {
- // Get frame interval.
- WindowManager wm =
- (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
- Display dpy = wm.getDefaultDisplay();
- double refreshRate = dpy.getRefreshRate();
- double frameIntervalMs = 1000.0 / refreshRate;
-
- double uiLoadTime = activity.mUILoadTime;
double[] setUpTimes = activity.mSetUpTimes;
double[] updateTimes = activity.mUpdateTimes;
double[] renderTimes = activity.mRenderTimes;
- double uiLoadScore = NEXUS_REF_UI_LOAD / uiLoadTime;
- double setUpScore = 0;// Lower better
- for (int i = 0; i < 4; i++) {
- setUpScore += NEXUS_REF_SET_UP[i] / setUpTimes[i];
- }
-
// Calculate update and render average.
double updateSum = updateTimes[0];
double renderSum = renderTimes[0];
- double[] renderIntervals = new double[NUM_FRAMES - 1];
for (int i = 0; i < NUM_FRAMES - 1; i++) {
updateSum += updateTimes[i + 1];
renderSum += renderTimes[i + 1];
- renderIntervals[i] = renderTimes[i + 1] - renderTimes[i];
}
double updateAverage = updateSum / NUM_FRAMES;
- double updateScore = NEXUS_REF_UPDATE_AVG / updateAverage;
double renderAverage = renderSum / NUM_FRAMES;
- double renderScore = NEXUS_REF_RENDER_AVG / (renderAverage / frameIntervalMs);
- // Calculate jankiness.
- int numJanks = 0;
- double totalJanks = 0.0;
- double[] janks = new double[NUM_FRAMES - 2];
- for (int i = 0; i < NUM_FRAMES - 2; i++) {
- double delta = renderIntervals[i + 1] - renderIntervals[i];
- janks[i] = Math.max(delta / frameIntervalMs, 0.0);
- if (janks[i] > 0) {
- numJanks++;
- }
- totalJanks += janks[i];
- }
-
- getReportLog().printValue(
- "UI Load Time", uiLoadTime, ResultType.LOWER_BETTER, ResultUnit.MS);
- getReportLog().printValue(
- "UI Load Score", uiLoadScore, ResultType.HIGHER_BETTER,
- ResultUnit.SCORE);
+ /* Held back for now
getReportLog().printArray(
"Set Up Times", setUpTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
- getReportLog().printValue(
- "Set Up Score", setUpScore, ResultType.HIGHER_BETTER, ResultUnit.SCORE);
getReportLog().printArray(
"Update Times", updateTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
getReportLog().printValue(
"Update Time Average", updateAverage, ResultType.LOWER_BETTER,
ResultUnit.MS);
- getReportLog().printValue("Update Score", updateScore, ResultType.HIGHER_BETTER,
- ResultUnit.SCORE);
getReportLog().printArray(
"Render Times", renderTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
getReportLog().printValue(
"Render Time Average", renderAverage, ResultType.LOWER_BETTER,
ResultUnit.MS);
- getReportLog().printValue("Render Score", renderScore, ResultType.HIGHER_BETTER,
- ResultUnit.SCORE);
- getReportLog().printValue(
- "Num Janks", numJanks, ResultType.LOWER_BETTER, ResultUnit.COUNT);
- getReportLog().printValue(
- "Total Jank", totalJanks, ResultType.LOWER_BETTER, ResultUnit.COUNT);
- score = (uiLoadScore + setUpScore + updateScore + renderScore) / 4.0f;
+ totalTime = setUpTimes[0] + setUpTimes[1] + setUpTimes[2] +
+ setUpTimes[3] + updateAverage + renderAverage;
+ */
+ } else {
+ // TODO benchmark failed to run
}
- getReportLog()
- .printSummary("Score", score, ResultType.HIGHER_BETTER, ResultUnit.SCORE);
+ /*
+ getReportLog().printSummary(
+ "Total Time", totalTime, ResultType.LOWER_BETTER, ResultUnit.MS);
+ */
activity.finish();
}
}
diff --git a/tests/jni/Android.mk b/tests/jni/Android.mk
index 39aafa1..0f7511e 100644
--- a/tests/jni/Android.mk
+++ b/tests/jni/Android.mk
@@ -23,6 +23,7 @@
LOCAL_SRC_FILES := \
CtsJniOnLoad.cpp \
+ android_os_cts_OSFeatures.cpp \
android_os_cts_FileUtils.cpp \
android_net_cts_NetlinkSocket.cpp
diff --git a/tests/jni/CtsJniOnLoad.cpp b/tests/jni/CtsJniOnLoad.cpp
index d029b2d..99ea37e 100644
--- a/tests/jni/CtsJniOnLoad.cpp
+++ b/tests/jni/CtsJniOnLoad.cpp
@@ -20,6 +20,8 @@
extern int register_android_os_cts_CpuFeatures(JNIEnv*);
+extern int register_android_os_cts_OSFeatures(JNIEnv*);
+
extern int register_android_os_cts_FileUtils(JNIEnv*);
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
@@ -33,6 +35,10 @@
return JNI_ERR;
}
+ if (register_android_os_cts_OSFeatures(env)) {
+ return JNI_ERR;
+ }
+
if (register_android_os_cts_FileUtils(env)) {
return JNI_ERR;
}
diff --git a/tests/jni/android_os_cts_OSFeatures.cpp b/tests/jni/android_os_cts_OSFeatures.cpp
new file mode 100644
index 0000000..4ee8454
--- /dev/null
+++ b/tests/jni/android_os_cts_OSFeatures.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <jni.h>
+#include <sys/prctl.h>
+
+jint android_os_cts_OSFeatures_getNoNewPrivs(JNIEnv* env, jobject thiz)
+{
+ return prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0);
+}
+
+jint android_os_cts_OSFeatures_prctlCapBsetRead(JNIEnv* env, jobject thiz, jint i)
+{
+ return prctl(PR_CAPBSET_READ, i, 0, 0, 0);
+}
+
+static JNINativeMethod gMethods[] = {
+ { "getNoNewPrivs", "()I",
+ (void *) android_os_cts_OSFeatures_getNoNewPrivs },
+ { "prctlCapBsetRead", "(I)I",
+ (void *) android_os_cts_OSFeatures_prctlCapBsetRead },
+};
+
+int register_android_os_cts_OSFeatures(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/os/cts/OSFeatures");
+
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/src/android/os/cts/OSFeatures.java b/tests/src/android/os/cts/OSFeatures.java
new file mode 100644
index 0000000..fd30f58
--- /dev/null
+++ b/tests/src/android/os/cts/OSFeatures.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.cts;
+
+public class OSFeatures {
+ static {
+ System.loadLibrary("cts_jni");
+ }
+
+ public static native int getNoNewPrivs();
+ public static native int prctlCapBsetRead(int i);
+}
diff --git a/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java b/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java
index 88d529c..01776e5 100644
--- a/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java
+++ b/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java
@@ -92,9 +92,6 @@
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
mUpdateCounter++;
if (mUpdateCounter % CAPTURE_SCREEN_INTERVAL == 0) {
- Canvas canvas = mTextureView.lockCanvas();
- canvas.drawColor(0xffff0000);
- mTextureView.unlockCanvasAndPost(canvas);
Bitmap bitmap = mTextureView.getBitmap();
Assert.assertEquals(mHeight, bitmap.getHeight());
Assert.assertEquals(mWidth, bitmap.getWidth());
diff --git a/tests/tests/keystore/Android.mk b/tests/tests/keystore/Android.mk
new file mode 100644
index 0000000..62c3301
--- /dev/null
+++ b/tests/tests/keystore/Android.mk
@@ -0,0 +1,33 @@
+# Copyright 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsKeystoreTestCases
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/keystore/AndroidManifest.xml b/tests/tests/keystore/AndroidManifest.xml
new file mode 100644
index 0000000..a19c983
--- /dev/null
+++ b/tests/tests/keystore/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.keystore">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+ android:targetPackage="com.android.cts.keystore"
+ android:label="CTS tests of com.android.cts.keystore"/>
+
+</manifest>
+
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java
new file mode 100644
index 0000000..ecc06c6
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.security.KeyPairGeneratorSpec;
+import android.test.AndroidTestCase;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
+ private KeyPairGenerator mGenerator;
+
+ private KeyStore mKeyStore;
+
+ private static final String TEST_ALIAS_1 = "test1";
+
+ private static final String TEST_ALIAS_2 = "test2";
+
+ private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
+
+ private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
+
+ private static final BigInteger TEST_SERIAL_1 = BigInteger.ONE;
+
+ private static final BigInteger TEST_SERIAL_2 = BigInteger.valueOf(2L);
+
+ private static final long NOW_MILLIS = System.currentTimeMillis();
+
+ /* We have to round this off because X509v3 doesn't store milliseconds. */
+ private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
+
+ @SuppressWarnings("deprecation")
+ private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
+
+ @Override
+ protected void setUp() throws Exception {
+ mKeyStore = KeyStore.getInstance("AndroidKeyStore");
+ mKeyStore.load(null, null);
+
+ mGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
+ }
+
+ public void testKeyPairGenerator_Initialize_Params_Unencrypted_Success() throws Exception {
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+ }
+
+ public void testKeyPairGenerator_Initialize_KeySize_Unencrypted_Failure() throws Exception {
+ try {
+ mGenerator.initialize(1024);
+ fail("KeyPairGenerator should not support setting the key size");
+ } catch (IllegalArgumentException success) {
+ }
+ }
+
+ public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Unencrypted_Failure()
+ throws Exception {
+ try {
+ mGenerator.initialize(1024, new SecureRandom());
+ fail("KeyPairGenerator should not support setting the key size");
+ } catch (IllegalArgumentException success) {
+ }
+ }
+
+ public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Unencrypted_Failure()
+ throws Exception {
+ mGenerator.initialize(
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build(),
+ new SecureRandom());
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_Unencrypted_Success() throws Exception {
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+
+ final KeyPair pair = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair);
+
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS);
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_Replaced_Unencrypted_Success()
+ throws Exception {
+ // Generate the first key
+ {
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1).setSubject(TEST_DN_1).setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW).setEndDate(NOW_PLUS_10_YEARS).build());
+ final KeyPair pair1 = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair1);
+ assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ // Replace the original key
+ {
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1).setSubject(TEST_DN_2).setSerialNumber(TEST_SERIAL_2)
+ .setStartDate(NOW).setEndDate(NOW_PLUS_10_YEARS).build());
+ final KeyPair pair2 = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair2);
+ assertKeyPairCorrect(pair2, TEST_ALIAS_1, TEST_DN_2, TEST_SERIAL_2, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_No_Collision_Unencrypted_Success()
+ throws Exception {
+ // Generate the first key
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+ final KeyPair pair1 = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair1);
+ assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+
+ // Generate the second key
+ mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_2)
+ .setSubject(TEST_DN_2)
+ .setSerialNumber(TEST_SERIAL_2)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+ final KeyPair pair2 = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair2);
+ assertKeyPairCorrect(pair2, TEST_ALIAS_2, TEST_DN_2, TEST_SERIAL_2, NOW,
+ NOW_PLUS_10_YEARS);
+
+ // Check the first key again
+ assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ private void assertKeyPairCorrect(KeyPair pair, String alias, X500Principal dn,
+ BigInteger serial, Date start, Date end) throws Exception {
+ final PublicKey pubKey = pair.getPublic();
+ assertNotNull("The PublicKey for the KeyPair should be not null", pubKey);
+
+ final PrivateKey privKey = pair.getPrivate();
+ assertNotNull("The PrivateKey for the KeyPair should be not null", privKey);
+
+ KeyStore.Entry entry = mKeyStore.getEntry(alias, null);
+ assertNotNull("Entry should exist", entry);
+
+ assertTrue("Entry should be a PrivateKeyEntry", entry instanceof KeyStore.PrivateKeyEntry);
+ KeyStore.PrivateKeyEntry privEntry = (KeyStore.PrivateKeyEntry) entry;
+
+ Certificate userCert = privEntry.getCertificate();
+ assertTrue("Certificate should be in X.509 format", userCert instanceof X509Certificate);
+
+ final X509Certificate x509userCert = (X509Certificate) userCert;
+
+ assertEquals("PublicKey used to sign certificate should match one returned in KeyPair",
+ pubKey, x509userCert.getPublicKey());
+
+ assertEquals("The Subject DN should be the one passed into the params", dn,
+ x509userCert.getSubjectDN());
+
+ assertEquals("The Issuer DN should be the same as the Subject DN", dn,
+ x509userCert.getIssuerDN());
+
+ assertEquals("The Serial should be the one passed into the params", serial,
+ x509userCert.getSerialNumber());
+
+ assertDateEquals("The notBefore date should be the one passed into the params", start,
+ x509userCert.getNotBefore());
+
+ assertDateEquals("The notAfter date should be the one passed into the params", end,
+ x509userCert.getNotAfter());
+
+ x509userCert.verify(pubKey);
+
+ Certificate[] chain = privEntry.getCertificateChain();
+ assertEquals("A list of CA certificates should not exist for the generated entry", 1,
+ chain.length);
+ }
+
+ private static void assertDateEquals(String message, Date date1, Date date2) throws Exception {
+ SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
+
+ String result1 = formatter.format(date1);
+ String result2 = formatter.format(date2);
+
+ assertEquals(message, result1, result2);
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
new file mode 100644
index 0000000..32f03ff
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
@@ -0,0 +1,1528 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.security.KeyStoreParameter;
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStore.TrustedCertificateEntry;
+import java.security.KeyStoreException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+public class AndroidKeyStoreTest extends AndroidTestCase {
+ private KeyStore mKeyStore;
+
+ private static final String TEST_ALIAS_1 = "test1";
+
+ private static final String TEST_ALIAS_2 = "test2";
+
+ private static final String TEST_ALIAS_3 = "test3";
+
+ /*
+ * The keys and certificates below are generated with:
+ *
+ * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+ * openssl req -newkey rsa:1024 -keyout userkey.pem -nodes -days 3650 -out userkey.req
+ * mkdir -p demoCA/newcerts
+ * touch demoCA/index.txt
+ * echo "01" > demoCA/serial
+ * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+ */
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_CA_1 = {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0xce, (byte) 0x30, (byte) 0x82,
+ (byte) 0x02, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xe1, (byte) 0x6a,
+ (byte) 0xa2, (byte) 0xf4, (byte) 0x2e, (byte) 0x55, (byte) 0x48, (byte) 0x0a,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31,
+ (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53,
+ (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43,
+ (byte) 0x41, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d,
+ (byte) 0x4d, (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61,
+ (byte) 0x69, (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65,
+ (byte) 0x77, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12,
+ (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69,
+ (byte) 0x64, (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74,
+ (byte) 0x20, (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73,
+ (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32,
+ (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x36,
+ (byte) 0x35, (byte) 0x35, (byte) 0x34, (byte) 0x34, (byte) 0x5a, (byte) 0x17,
+ (byte) 0x0d, (byte) 0x32, (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31,
+ (byte) 0x32, (byte) 0x31, (byte) 0x36, (byte) 0x35, (byte) 0x35, (byte) 0x34,
+ (byte) 0x34, (byte) 0x5a, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
+ (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41,
+ (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d,
+ (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69,
+ (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77,
+ (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41,
+ (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64,
+ (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20,
+ (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30,
+ (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
+ (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+ (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89,
+ (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xa3, (byte) 0x72,
+ (byte) 0xab, (byte) 0xd0, (byte) 0xe4, (byte) 0xad, (byte) 0x2f, (byte) 0xe7,
+ (byte) 0xe2, (byte) 0x79, (byte) 0x07, (byte) 0x36, (byte) 0x3d, (byte) 0x0c,
+ (byte) 0x8d, (byte) 0x42, (byte) 0x9a, (byte) 0x0a, (byte) 0x33, (byte) 0x64,
+ (byte) 0xb3, (byte) 0xcd, (byte) 0xb2, (byte) 0xd7, (byte) 0x3a, (byte) 0x42,
+ (byte) 0x06, (byte) 0x77, (byte) 0x45, (byte) 0x29, (byte) 0xe9, (byte) 0xcb,
+ (byte) 0xb7, (byte) 0x4a, (byte) 0xd6, (byte) 0xee, (byte) 0xad, (byte) 0x01,
+ (byte) 0x91, (byte) 0x9b, (byte) 0x0c, (byte) 0x59, (byte) 0xa1, (byte) 0x03,
+ (byte) 0xfa, (byte) 0xf0, (byte) 0x5a, (byte) 0x7c, (byte) 0x4f, (byte) 0xf7,
+ (byte) 0x8d, (byte) 0x36, (byte) 0x0f, (byte) 0x1f, (byte) 0x45, (byte) 0x7d,
+ (byte) 0x1b, (byte) 0x31, (byte) 0xa1, (byte) 0x35, (byte) 0x0b, (byte) 0x00,
+ (byte) 0xed, (byte) 0x7a, (byte) 0xb6, (byte) 0xc8, (byte) 0x4e, (byte) 0xa9,
+ (byte) 0x86, (byte) 0x4c, (byte) 0x7b, (byte) 0x99, (byte) 0x57, (byte) 0x41,
+ (byte) 0x12, (byte) 0xef, (byte) 0x6b, (byte) 0xbc, (byte) 0x3d, (byte) 0x60,
+ (byte) 0xf2, (byte) 0x99, (byte) 0x1a, (byte) 0xcd, (byte) 0xed, (byte) 0x56,
+ (byte) 0xa4, (byte) 0xe5, (byte) 0x36, (byte) 0x9f, (byte) 0x24, (byte) 0x1f,
+ (byte) 0xdc, (byte) 0x89, (byte) 0x40, (byte) 0xc8, (byte) 0x99, (byte) 0x92,
+ (byte) 0xab, (byte) 0x4a, (byte) 0xb5, (byte) 0x61, (byte) 0x45, (byte) 0x62,
+ (byte) 0xff, (byte) 0xa3, (byte) 0x45, (byte) 0x65, (byte) 0xaf, (byte) 0xf6,
+ (byte) 0x27, (byte) 0x30, (byte) 0x51, (byte) 0x0e, (byte) 0x0e, (byte) 0xeb,
+ (byte) 0x79, (byte) 0x0c, (byte) 0xbe, (byte) 0xb3, (byte) 0x0a, (byte) 0x6f,
+ (byte) 0x29, (byte) 0x06, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x51,
+ (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
+ (byte) 0x81, (byte) 0xb1, (byte) 0x30, (byte) 0x81, (byte) 0xae, (byte) 0x30,
+ (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e,
+ (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x33, (byte) 0x05,
+ (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60, (byte) 0xc7, (byte) 0xf9,
+ (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c, (byte) 0x8f, (byte) 0x6d,
+ (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e, (byte) 0x5d, (byte) 0x51,
+ (byte) 0x30, (byte) 0x7f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d,
+ (byte) 0x23, (byte) 0x04, (byte) 0x78, (byte) 0x30, (byte) 0x76, (byte) 0x80,
+ (byte) 0x14, (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f,
+ (byte) 0x60, (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73,
+ (byte) 0x5c, (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97,
+ (byte) 0x8e, (byte) 0x5d, (byte) 0x51, (byte) 0xa1, (byte) 0x53, (byte) 0xa4,
+ (byte) 0x51, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+ (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
+ (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
+ (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
+ (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
+ (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
+ (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
+ (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
+ (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x82, (byte) 0x09,
+ (byte) 0x00, (byte) 0xe1, (byte) 0x6a, (byte) 0xa2, (byte) 0xf4, (byte) 0x2e,
+ (byte) 0x55, (byte) 0x48, (byte) 0x0a, (byte) 0x30, (byte) 0x0c, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05,
+ (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30,
+ (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+ (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05,
+ (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00,
+ (byte) 0x8c, (byte) 0x30, (byte) 0x42, (byte) 0xfa, (byte) 0xeb, (byte) 0x1a,
+ (byte) 0x26, (byte) 0xeb, (byte) 0xda, (byte) 0x56, (byte) 0x32, (byte) 0xf2,
+ (byte) 0x9d, (byte) 0xa5, (byte) 0x24, (byte) 0xd8, (byte) 0x3a, (byte) 0xda,
+ (byte) 0x30, (byte) 0xa6, (byte) 0x8b, (byte) 0x46, (byte) 0xfe, (byte) 0xfe,
+ (byte) 0xdb, (byte) 0xf1, (byte) 0xe6, (byte) 0xe1, (byte) 0x7c, (byte) 0x1b,
+ (byte) 0xe7, (byte) 0x77, (byte) 0x00, (byte) 0xa1, (byte) 0x1c, (byte) 0x19,
+ (byte) 0x17, (byte) 0x73, (byte) 0xb0, (byte) 0xf0, (byte) 0x9d, (byte) 0xf3,
+ (byte) 0x4f, (byte) 0xb6, (byte) 0xbc, (byte) 0xc7, (byte) 0x47, (byte) 0x85,
+ (byte) 0x2a, (byte) 0x4a, (byte) 0xa1, (byte) 0xa5, (byte) 0x58, (byte) 0xf5,
+ (byte) 0xc5, (byte) 0x1a, (byte) 0x51, (byte) 0xb1, (byte) 0x04, (byte) 0x80,
+ (byte) 0xee, (byte) 0x3a, (byte) 0xec, (byte) 0x2f, (byte) 0xe1, (byte) 0xfd,
+ (byte) 0x58, (byte) 0xeb, (byte) 0xed, (byte) 0x82, (byte) 0x9e, (byte) 0x38,
+ (byte) 0xa3, (byte) 0x24, (byte) 0x75, (byte) 0xf7, (byte) 0x3e, (byte) 0xc2,
+ (byte) 0xc5, (byte) 0x27, (byte) 0xeb, (byte) 0x6f, (byte) 0x7b, (byte) 0x50,
+ (byte) 0xda, (byte) 0x43, (byte) 0xdc, (byte) 0x3b, (byte) 0x0b, (byte) 0x6f,
+ (byte) 0x78, (byte) 0x8f, (byte) 0xb0, (byte) 0x66, (byte) 0xe1, (byte) 0x12,
+ (byte) 0x87, (byte) 0x5f, (byte) 0x97, (byte) 0x7b, (byte) 0xca, (byte) 0x14,
+ (byte) 0x79, (byte) 0xf7, (byte) 0xe8, (byte) 0x6c, (byte) 0x72, (byte) 0xdb,
+ (byte) 0x91, (byte) 0x65, (byte) 0x17, (byte) 0x54, (byte) 0xe0, (byte) 0x74,
+ (byte) 0x1d, (byte) 0xac, (byte) 0x47, (byte) 0x04, (byte) 0x12, (byte) 0xe0,
+ (byte) 0xc3, (byte) 0x66, (byte) 0x19, (byte) 0x05, (byte) 0x2e, (byte) 0x7e,
+ (byte) 0xf1, (byte) 0x61
+ };
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_KEY_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
+ (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+ (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+ (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82,
+ (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
+ (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
+ (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
+ (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
+ (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
+ (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
+ (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
+ (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
+ (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
+ (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
+ (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
+ (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
+ (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
+ (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
+ (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
+ (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
+ (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
+ (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
+ (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
+ (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
+ (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
+ (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
+ (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
+ (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
+ (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
+ (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
+ (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
+ (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
+ (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
+ (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
+ (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
+ (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
+ (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
+ (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
+ (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
+ (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
+ (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
+ (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
+ (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
+ (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
+ (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
+ (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
+ (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
+ (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
+ (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
+ (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
+ (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
+ (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
+ (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
+ (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
+ (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
+ (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
+ (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
+ (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
+ (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
+ (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
+ (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
+ (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
+ (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
+ (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
+ (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
+ (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
+ (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
+ (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
+ (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
+ (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
+ (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
+ (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
+ (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
+ (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
+ (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
+ (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
+ (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
+ (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
+ (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
+ (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
+ (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
+ (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
+ (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
+ (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
+ (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
+ (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
+ (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
+ (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
+ (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
+ (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
+ (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
+ (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
+ (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
+ (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
+ (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
+ (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
+ (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
+ (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
+ (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
+ (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
+ (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
+ (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
+ (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
+ (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
+ (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
+ };
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl x509 -outform d -in usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_USER_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x95, (byte) 0x30, (byte) 0x82,
+ (byte) 0x01, (byte) 0xfe, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x30, (byte) 0x0d,
+ (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+ (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05,
+ (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+ (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
+ (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
+ (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
+ (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
+ (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
+ (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
+ (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
+ (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30, (byte) 0x1e,
+ (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32, (byte) 0x30, (byte) 0x38,
+ (byte) 0x31, (byte) 0x34, (byte) 0x32, (byte) 0x33, (byte) 0x32, (byte) 0x35,
+ (byte) 0x34, (byte) 0x38, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32,
+ (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x32, (byte) 0x32,
+ (byte) 0x33, (byte) 0x32, (byte) 0x35, (byte) 0x34, (byte) 0x38, (byte) 0x5a,
+ (byte) 0x30, (byte) 0x55, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13,
+ (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08,
+ (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31, (byte) 0x1b,
+ (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e, (byte) 0x64,
+ (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20, (byte) 0x54,
+ (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43, (byte) 0x61,
+ (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x31, (byte) 0x1c, (byte) 0x30,
+ (byte) 0x1a, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03,
+ (byte) 0x13, (byte) 0x13, (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76,
+ (byte) 0x65, (byte) 0x72, (byte) 0x31, (byte) 0x2e, (byte) 0x65, (byte) 0x78,
+ (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x65, (byte) 0x2e,
+ (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x81, (byte) 0x9f,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8d,
+ (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02, (byte) 0x81,
+ (byte) 0x81, (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6,
+ (byte) 0x5b, (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c,
+ (byte) 0x66, (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86,
+ (byte) 0x8a, (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3,
+ (byte) 0x02, (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08,
+ (byte) 0xf3, (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04,
+ (byte) 0x6d, (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f,
+ (byte) 0x67, (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c,
+ (byte) 0xcb, (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30,
+ (byte) 0xe2, (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5,
+ (byte) 0x79, (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b,
+ (byte) 0xce, (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb,
+ (byte) 0x08, (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff,
+ (byte) 0x3b, (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9,
+ (byte) 0xc4, (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29,
+ (byte) 0x0d, (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b,
+ (byte) 0x23, (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78,
+ (byte) 0x08, (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5,
+ (byte) 0xf1, (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19,
+ (byte) 0xb4, (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03,
+ (byte) 0x16, (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce,
+ (byte) 0x9e, (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03,
+ (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3, (byte) 0x7b, (byte) 0x30,
+ (byte) 0x79, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00,
+ (byte) 0x30, (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x60, (byte) 0x86,
+ (byte) 0x48, (byte) 0x01, (byte) 0x86, (byte) 0xf8, (byte) 0x42, (byte) 0x01,
+ (byte) 0x0d, (byte) 0x04, (byte) 0x1f, (byte) 0x16, (byte) 0x1d, (byte) 0x4f,
+ (byte) 0x70, (byte) 0x65, (byte) 0x6e, (byte) 0x53, (byte) 0x53, (byte) 0x4c,
+ (byte) 0x20, (byte) 0x47, (byte) 0x65, (byte) 0x6e, (byte) 0x65, (byte) 0x72,
+ (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x64, (byte) 0x20, (byte) 0x43,
+ (byte) 0x65, (byte) 0x72, (byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69,
+ (byte) 0x63, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x1d,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04,
+ (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x32, (byte) 0xa1, (byte) 0x1e,
+ (byte) 0x6b, (byte) 0x69, (byte) 0x04, (byte) 0xfe, (byte) 0xb3, (byte) 0xcd,
+ (byte) 0xf8, (byte) 0xbb, (byte) 0x14, (byte) 0xcd, (byte) 0xff, (byte) 0xd4,
+ (byte) 0x16, (byte) 0xc3, (byte) 0xab, (byte) 0x44, (byte) 0x2f, (byte) 0x30,
+ (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23,
+ (byte) 0x04, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14,
+ (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60,
+ (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c,
+ (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e,
+ (byte) 0x5d, (byte) 0x51, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
+ (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
+ (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+ (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x46, (byte) 0x42, (byte) 0xef,
+ (byte) 0x56, (byte) 0x89, (byte) 0x78, (byte) 0x90, (byte) 0x38, (byte) 0x24,
+ (byte) 0x9f, (byte) 0x8c, (byte) 0x7a, (byte) 0xce, (byte) 0x7a, (byte) 0xa5,
+ (byte) 0xb5, (byte) 0x1e, (byte) 0x74, (byte) 0x96, (byte) 0x34, (byte) 0x49,
+ (byte) 0x8b, (byte) 0xed, (byte) 0x44, (byte) 0xb3, (byte) 0xc9, (byte) 0x05,
+ (byte) 0xd7, (byte) 0x48, (byte) 0x55, (byte) 0x52, (byte) 0x59, (byte) 0x15,
+ (byte) 0x0b, (byte) 0xaa, (byte) 0x16, (byte) 0x86, (byte) 0xd2, (byte) 0x8e,
+ (byte) 0x16, (byte) 0x99, (byte) 0xe8, (byte) 0x5f, (byte) 0x11, (byte) 0x71,
+ (byte) 0x42, (byte) 0x55, (byte) 0xd1, (byte) 0xc4, (byte) 0x6f, (byte) 0x2e,
+ (byte) 0xa9, (byte) 0x64, (byte) 0x6f, (byte) 0xd8, (byte) 0xfd, (byte) 0x43,
+ (byte) 0x13, (byte) 0x24, (byte) 0xaa, (byte) 0x67, (byte) 0xe6, (byte) 0xf5,
+ (byte) 0xca, (byte) 0x80, (byte) 0x5e, (byte) 0x3a, (byte) 0x3e, (byte) 0xcc,
+ (byte) 0x4f, (byte) 0xba, (byte) 0x87, (byte) 0xe6, (byte) 0xae, (byte) 0xbf,
+ (byte) 0x8f, (byte) 0xd5, (byte) 0x28, (byte) 0x38, (byte) 0x58, (byte) 0x30,
+ (byte) 0x24, (byte) 0xf6, (byte) 0x53, (byte) 0x5b, (byte) 0x41, (byte) 0x53,
+ (byte) 0xe6, (byte) 0x45, (byte) 0xbc, (byte) 0xbe, (byte) 0xe6, (byte) 0xbb,
+ (byte) 0x5d, (byte) 0xd8, (byte) 0xa7, (byte) 0xf9, (byte) 0x64, (byte) 0x99,
+ (byte) 0x04, (byte) 0x43, (byte) 0x75, (byte) 0xd7, (byte) 0x2d, (byte) 0x32,
+ (byte) 0x0a, (byte) 0x94, (byte) 0xaf, (byte) 0x06, (byte) 0x34, (byte) 0xae,
+ (byte) 0x46, (byte) 0xbd, (byte) 0xda, (byte) 0x00, (byte) 0x0e, (byte) 0x25,
+ (byte) 0xc2, (byte) 0xf7, (byte) 0xc9, (byte) 0xc3, (byte) 0x65, (byte) 0xd2,
+ (byte) 0x08, (byte) 0x41, (byte) 0x0a, (byte) 0xf3, (byte) 0x72
+ };
+
+ /**
+ * The amount of time to allow before and after expected time for variance
+ * in timing tests.
+ */
+ private static final long SLOP_TIME_MILLIS = 15000L;
+
+ @Override
+ protected void setUp() throws Exception {
+ // Wipe any existing entries in the KeyStore
+ KeyStore ksTemp = KeyStore.getInstance("AndroidKeyStore");
+ ksTemp.load(null, null);
+ Enumeration<String> aliases = ksTemp.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ ksTemp.deleteEntry(alias);
+ }
+
+ // Get a new instance because some tests need it uninitialized
+ mKeyStore = KeyStore.getInstance("AndroidKeyStore");
+ }
+
+ private PrivateKey generatePrivateKey(byte[] fakeKey1) throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ return kf.generatePrivate(new PKCS8EncodedKeySpec(fakeKey1));
+ }
+
+ private Certificate generateCertificate(byte[] fakeUser1) throws Exception {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ return cf.generateCertificate(new ByteArrayInputStream(fakeUser1));
+ }
+
+ private PrivateKeyEntry makeUserKey1() throws Exception {
+ return new KeyStore.PrivateKeyEntry(generatePrivateKey(FAKE_KEY_1), new Certificate[] {
+ generateCertificate(FAKE_USER_1), generateCertificate(FAKE_CA_1)
+ });
+ }
+
+ private Entry makeCa1() throws Exception {
+ return new KeyStore.TrustedCertificateEntry(generateCertificate(FAKE_CA_1));
+ }
+
+ private void assertAliases(final String[] expectedAliases) throws KeyStoreException {
+ final Enumeration<String> aliases = mKeyStore.aliases();
+ int count = 0;
+
+ final Set<String> expectedSet = new HashSet<String>();
+ expectedSet.addAll(Arrays.asList(expectedAliases));
+
+ while (aliases.hasMoreElements()) {
+ count++;
+ final String alias = aliases.nextElement();
+ assertTrue("The alias should be in the expected set", expectedSet.contains(alias));
+ expectedSet.remove(alias);
+ }
+ assertTrue("The expected set and actual set should be exactly equal", expectedSet.isEmpty());
+ assertEquals("There should be the correct number of keystore entries",
+ expectedAliases.length, count);
+ }
+
+ public void testKeyStore_Aliases_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertAliases(new String[] {});
+
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ mKeyStore.setEntry(TEST_ALIAS_2, makeCa1(), null);
+
+ assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
+ }
+
+ public void testKeyStore_Aliases_NotInitialized_Unencrypted_Failure() throws Exception {
+ try {
+ mKeyStore.aliases();
+ fail("KeyStore should throw exception when not initialized");
+ } catch (KeyStoreException success) {
+ }
+ }
+
+ public void testKeyStore_ContainsAliases_PrivateAndCA_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertAliases(new String[] {});
+
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ assertTrue("Should contain generated private key", mKeyStore.containsAlias(TEST_ALIAS_1));
+
+ mKeyStore.setEntry(TEST_ALIAS_2, makeCa1(), null);
+
+ assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
+
+ assertFalse("Should not contain unadded certificate alias",
+ mKeyStore.containsAlias(TEST_ALIAS_3));
+ }
+
+ public void testKeyStore_ContainsAliases_CAOnly_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ mKeyStore.setEntry(TEST_ALIAS_2, makeCa1(), null);
+
+ assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
+ }
+
+ public void testKeyStore_ContainsAliases_NonExistent_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertFalse("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_DeleteEntry_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ // TEST_ALIAS_2
+ mKeyStore.setCertificateEntry(TEST_ALIAS_2, generateCertificate(FAKE_CA_1));
+
+ // TEST_ALIAS_3
+ mKeyStore.setCertificateEntry(TEST_ALIAS_3, generateCertificate(FAKE_CA_1));
+
+ assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
+
+ mKeyStore.deleteEntry(TEST_ALIAS_1);
+
+ assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 });
+
+ mKeyStore.deleteEntry(TEST_ALIAS_3);
+
+ assertAliases(new String[] { TEST_ALIAS_2 });
+
+ mKeyStore.deleteEntry(TEST_ALIAS_2);
+
+ assertAliases(new String[] { });
+ }
+
+ public void testKeyStore_DeleteEntry_EmptyStore_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // Should not throw when a non-existent entry is requested for delete.
+ mKeyStore.deleteEntry(TEST_ALIAS_1);
+ }
+
+ public void testKeyStore_DeleteEntry_NonExistent_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ // Should not throw when a non-existent entry is requested for delete.
+ mKeyStore.deleteEntry(TEST_ALIAS_2);
+ }
+
+ public void testKeyStore_GetCertificate_Single_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, generateCertificate(FAKE_CA_1));
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ assertNull("Certificate should not exist in keystore",
+ mKeyStore.getCertificate(TEST_ALIAS_2));
+
+ Certificate retrieved = mKeyStore.getCertificate(TEST_ALIAS_1);
+
+ assertNotNull("Retrieved certificate should not be null", retrieved);
+
+ CertificateFactory f = CertificateFactory.getInstance("X.509");
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ assertEquals("Actual and retrieved certificates should be the same", actual, retrieved);
+ }
+
+ public void testKeyStore_GetCertificate_NonExist_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("Certificate should not exist in keystore",
+ mKeyStore.getCertificate(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_GetCertificateAlias_CAEntry_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ Certificate cert = generateCertificate(FAKE_CA_1);
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, cert);
+
+ assertEquals("Stored certificate alias should be found", TEST_ALIAS_1,
+ mKeyStore.getCertificateAlias(cert));
+ }
+
+ public void testKeyStore_GetCertificateAlias_PrivateKeyEntry_Unencrypted_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ CertificateFactory f = CertificateFactory.getInstance("X.509");
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+
+ assertEquals("Stored certificate alias should be found", TEST_ALIAS_1,
+ mKeyStore.getCertificateAlias(actual));
+ }
+
+ public void testKeyStore_GetCertificateAlias_CAEntry_WithPrivateKeyUsingCA_Unencrypted_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ Certificate actual = generateCertificate(FAKE_CA_1);
+
+ // Insert TrustedCertificateEntry with CA name
+ mKeyStore.setCertificateEntry(TEST_ALIAS_2, actual);
+
+ // Insert PrivateKeyEntry that uses the same CA
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ assertEquals("Stored certificate alias should be found", TEST_ALIAS_2,
+ mKeyStore.getCertificateAlias(actual));
+ }
+
+ public void testKeyStore_GetCertificateAlias_NonExist_Empty_Unencrypted_Failure()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ CertificateFactory f = CertificateFactory.getInstance("X.509");
+ Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ assertNull("Stored certificate alias should not be found",
+ mKeyStore.getCertificateAlias(actual));
+ }
+
+ public void testKeyStore_GetCertificateAlias_NonExist_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ Certificate ca = generateCertificate(FAKE_CA_1);
+
+ // Insert TrustedCertificateEntry with CA name
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, ca);
+
+ Certificate userCert = generateCertificate(FAKE_USER_1);
+
+ assertNull("Stored certificate alias should be found",
+ mKeyStore.getCertificateAlias(userCert));
+ }
+
+ public void testKeyStore_GetCertificateChain_SingleLength_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ Certificate[] expected = new Certificate[2];
+ expected[0] = generateCertificate(FAKE_USER_1);
+ expected[1] = generateCertificate(FAKE_CA_1);
+
+ Certificate[] actual = mKeyStore.getCertificateChain(TEST_ALIAS_1);
+
+ assertNotNull("Returned certificate chain should not be null", actual);
+ assertEquals("Returned certificate chain should be correct size", expected.length,
+ actual.length);
+ assertEquals("First certificate should be user certificate", expected[0], actual[0]);
+ assertEquals("Second certificate should be CA certificate", expected[1], actual[1]);
+
+ // Negative test when keystore is populated.
+ assertNull("Stored certificate alias should not be found",
+ mKeyStore.getCertificateChain(TEST_ALIAS_2));
+ }
+
+ public void testKeyStore_GetCertificateChain_NonExist_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("Stored certificate alias should not be found",
+ mKeyStore.getCertificateChain(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_GetCreationDate_PrivateKeyEntry_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ Date now = new Date();
+ Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
+
+ Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS);
+ Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS);
+
+ assertTrue("Time should be close to current time", actual.before(expectedBefore));
+ assertTrue("Time should be close to current time", actual.after(expectedAfter));
+ }
+
+ public void testKeyStore_GetCreationDate_CAEntry_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // Insert TrustedCertificateEntry with CA name
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, generateCertificate(FAKE_CA_1));
+
+ Date now = new Date();
+ Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
+ assertNotNull("Certificate should be found", actual);
+
+ Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS);
+ Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS);
+
+ assertTrue("Time should be close to current time", actual.before(expectedBefore));
+ assertTrue("Time should be close to current time", actual.after(expectedAfter));
+ }
+
+ public void testKeyStore_GetEntry_NullParams_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Entry should exist", entry);
+
+ assertTrue("Should be a PrivateKeyEntry", entry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+ assertPrivateKeyEntryEquals(keyEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, byte[] key, byte[] cert,
+ byte[] ca) throws Exception {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(key));
+
+ CertificateFactory certFact = CertificateFactory.getInstance("X.509");
+ Certificate expectedCert = certFact.generateCertificate(new ByteArrayInputStream(cert));
+
+ final Collection<Certificate> expectedChain;
+ if (ca != null) {
+ expectedChain = (Collection<Certificate>) certFact
+ .generateCertificates(new ByteArrayInputStream(ca));
+ } else {
+ expectedChain = null;
+ }
+
+ assertPrivateKeyEntryEquals(keyEntry, expectedKey, expectedCert, expectedChain);
+ }
+
+ private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, PrivateKey expectedKey,
+ Certificate expectedCert, Collection<Certificate> expectedChain) throws Exception {
+ assertEquals("Returned PrivateKey should be what we inserted",
+ ((RSAPrivateKey) expectedKey).getModulus(),
+ ((RSAPrivateKey) keyEntry.getPrivateKey()).getModulus());
+
+ assertEquals("Returned Certificate should be what we inserted", expectedCert,
+ keyEntry.getCertificate());
+
+ Certificate[] actualChain = keyEntry.getCertificateChain();
+
+ assertEquals("First certificate in chain should be user cert", expectedCert, actualChain[0]);
+
+ if (expectedChain == null) {
+ assertEquals("Certificate chain should not include CAs", 1, actualChain.length);
+ } else {
+ assertEquals("Chains should be the same size", expectedChain.size() + 1,
+ actualChain.length);
+ int i = 1;
+ final Iterator<Certificate> it = expectedChain.iterator();
+ while (it.hasNext() && i < actualChain.length) {
+ assertEquals("CA chain certificate should equal what we put in", it.next(),
+ actualChain[i++]);
+ }
+ }
+ }
+
+ public void testKeyStore_GetEntry_Nonexistent_NullParams_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("A non-existent entry should return null",
+ mKeyStore.getEntry(TEST_ALIAS_1, null));
+ }
+
+ public void testKeyStore_GetKey_NoPassword_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
+ assertNotNull("Key should exist", key);
+
+ assertTrue("Should be a RSAPrivateKey", key instanceof RSAPrivateKey);
+
+ RSAPrivateKey actualKey = (RSAPrivateKey) key;
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ assertEquals("Inserted key should be same as retrieved key",
+ ((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
+ }
+
+ public void testKeyStore_GetKey_Certificate_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ // Insert TrustedCertificateEntry with CA name
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, generateCertificate(FAKE_CA_1));
+
+ assertNull("Certificate entries should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
+ }
+
+ public void testKeyStore_GetKey_NonExistent_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("A non-existent entry should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
+ }
+
+ public void testKeyStore_GetProvider_Unencrypted_Success() throws Exception {
+ assertEquals("AndroidKeyStore", mKeyStore.getProvider().getName());
+ }
+
+ public void testKeyStore_GetType_Unencrypted_Success() throws Exception {
+ assertEquals("AndroidKeyStore", mKeyStore.getType());
+ }
+
+ public void testKeyStore_IsCertificateEntry_CA_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // Insert TrustedCertificateEntry with CA name
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, generateCertificate(FAKE_CA_1));
+
+ assertTrue("Should return true for CA certificate",
+ mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsCertificateEntry_PrivateKey_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ assertFalse("Should return false for PrivateKeyEntry",
+ mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsCertificateEntry_NonExist_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertFalse("Should return false for non-existent entry",
+ mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsKeyEntry_PrivateKey_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // TEST_ALIAS_1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ assertTrue("Should return true for PrivateKeyEntry", mKeyStore.isKeyEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsKeyEntry_CA_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, generateCertificate(FAKE_CA_1));
+
+ assertFalse("Should return false for CA certificate", mKeyStore.isKeyEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsKeyEntry_NonExist_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertFalse("Should return false for non-existent entry",
+ mKeyStore.isKeyEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_SetCertificate_CA_Unencrypted_Success() throws Exception {
+ final Certificate actual = generateCertificate(FAKE_CA_1);
+
+ mKeyStore.load(null, null);
+
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, actual);
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ Certificate retrieved = mKeyStore.getCertificate(TEST_ALIAS_1);
+
+ assertEquals("Retrieved certificate should be the same as the one inserted", actual,
+ retrieved);
+ }
+
+ public void testKeyStore_SetCertificate_CAExists_Overwrite_Unencrypted_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, generateCertificate(FAKE_CA_1));
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ final Certificate cert = generateCertificate(FAKE_CA_1);
+
+ // TODO have separate FAKE_CA for second test
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, cert);
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+ }
+
+ public void testKeyStore_SetCertificate_PrivateKeyExists_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ final Certificate cert = generateCertificate(FAKE_CA_1);
+
+ try {
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, cert);
+ fail("Should throw when trying to overwrite a PrivateKey entry with a Certificate");
+ } catch (KeyStoreException success) {
+ }
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Params_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry entry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ try {
+ mKeyStore.setEntry(TEST_ALIAS_1, entry,
+ new KeyStoreParameter.Builder(getContext())
+ .setEncryptionRequired(true)
+ .build());
+ fail("Shouldn't be able to insert encrypted entry when KeyStore uninitialized");
+ } catch (KeyStoreException expected) {
+ }
+
+ assertNull(mKeyStore.getEntry(TEST_ALIAS_1, null));
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_PrivateKeyEntry_Unencrypted_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ final KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ // Start with PrivateKeyEntry
+ {
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ // TODO make entirely new test vector for the overwrite
+ // Replace with PrivateKeyEntry
+ {
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+ }
+
+ public void testKeyStore_SetEntry_CAEntry_Overwrites_PrivateKeyEntry_Unencrypted_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ // Start with TrustedCertificateEntry
+ {
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+ actualEntry instanceof TrustedCertificateEntry);
+ TrustedCertificateEntry actualCertEntry = (TrustedCertificateEntry) actualEntry;
+ assertEquals("Stored and retrieved certificates should be the same",
+ expectedCertEntry.getTrustedCertificate(),
+ actualCertEntry.getTrustedCertificate());
+ }
+
+ // Replace with PrivateKeyEntry
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+ assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_CAEntry_Unencrypted_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ // Start with PrivateKeyEntry
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = caCert;
+
+ PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+ assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ // Replace with TrustedCertificateEntry
+ {
+ TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+ actualEntry instanceof TrustedCertificateEntry);
+ TrustedCertificateEntry actualCertEntry = (TrustedCertificateEntry) actualEntry;
+ assertEquals("Stored and retrieved certificates should be the same",
+ expectedCertEntry.getTrustedCertificate(),
+ actualCertEntry.getTrustedCertificate());
+ }
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_ShortPrivateKeyEntry_Unencrypted_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ // Start with PrivateKeyEntry
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = caCert;
+
+ PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+ assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ // Replace with PrivateKeyEntry that has no chain
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] expectedChain = new Certificate[1];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+
+ PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+ assertPrivateKeyEntryEquals(actualPrivEntry, FAKE_KEY_1, FAKE_USER_1, null);
+ }
+ }
+
+ public void testKeyStore_SetEntry_CAEntry_Overwrites_CAEntry_Unencrypted_Success()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ // Insert TrustedCertificateEntry
+ {
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+ actualEntry instanceof TrustedCertificateEntry);
+ TrustedCertificateEntry actualCertEntry = (TrustedCertificateEntry) actualEntry;
+ assertEquals("Stored and retrieved certificates should be the same",
+ expectedCertEntry.getTrustedCertificate(),
+ actualCertEntry.getTrustedCertificate());
+ }
+
+ // Replace with TrustedCertificateEntry of USER
+ {
+ final Certificate userCert = f
+ .generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+
+ TrustedCertificateEntry expectedUserEntry = new TrustedCertificateEntry(userCert);
+ mKeyStore.setEntry(TEST_ALIAS_1, expectedUserEntry, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+ assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+ actualEntry instanceof TrustedCertificateEntry);
+ TrustedCertificateEntry actualUserEntry = (TrustedCertificateEntry) actualEntry;
+ assertEquals("Stored and retrieved certificates should be the same",
+ expectedUserEntry.getTrustedCertificate(),
+ actualUserEntry.getTrustedCertificate());
+ }
+ }
+
+ public void testKeyStore_SetKeyEntry_ProtectedKey_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] chain = new Certificate[2];
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[1] = caCert;
+
+ try {
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, "foo".toCharArray(), chain);
+ fail("Should fail when a password is specified");
+ } catch (KeyStoreException success) {
+ }
+ }
+
+ public void testKeyStore_SetKeyEntry_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] chain = new Certificate[2];
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[1] = caCert;
+
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ public void testKeyStore_SetKeyEntry_Replaced_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ // Insert initial key
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] chain = new Certificate[2];
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[1] = caCert;
+
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ // TODO make a separate key
+ // Replace key
+ {
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+ final Certificate[] chain = new Certificate[2];
+ chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ chain[1] = caCert;
+
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+ }
+
+ public void testKeyStore_SetKeyEntry_ReplacedChain_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ // Create key #1
+ {
+ KeyStore.PrivateKeyEntry privEntry = makeUserKey1();
+ mKeyStore.setEntry(TEST_ALIAS_1, privEntry, null);
+
+ Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+
+ assertTrue(entry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+ ArrayList<Certificate> chain = new ArrayList<Certificate>();
+ chain.add(generateCertificate(FAKE_CA_1));
+ assertPrivateKeyEntryEquals(keyEntry, privEntry.getPrivateKey(),
+ privEntry.getCertificate(), chain);
+ }
+
+ // Replace key #1 with new chain
+ {
+ Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
+
+ assertTrue(key instanceof PrivateKey);
+
+ PrivateKey expectedKey = (PrivateKey) key;
+
+ Certificate expectedCert = generateCertificate(FAKE_USER_1);
+
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, expectedKey, null,
+ new Certificate[] { expectedCert });
+
+ Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+
+ assertTrue(entry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+ assertPrivateKeyEntryEquals(keyEntry, expectedKey, expectedCert, null);
+ }
+ }
+
+ public void testKeyStore_SetKeyEntry_ReplacedChain_DifferentPrivateKey_Unencrypted_Failure()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ // Create key #1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ // Create key #2
+ mKeyStore.setEntry(TEST_ALIAS_2, makeUserKey1(), null);
+
+
+ // Replace key #1 with key #2
+ {
+ Key key1 = mKeyStore.getKey(TEST_ALIAS_2, null);
+
+ Certificate cert = generateCertificate(FAKE_USER_1);
+
+ try {
+ mKeyStore.setKeyEntry(TEST_ALIAS_1, key1, null, new Certificate[] { cert });
+ fail("Should not allow setting of KeyEntry with wrong PrivaetKey");
+ } catch (KeyStoreException success) {
+ }
+ }
+ }
+
+ public void testKeyStore_SetKeyEntry_ReplacedWithSame_UnencryptedToUnencrypted_Failure()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ // Create key #1
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ // Replace with same
+ Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ mKeyStore.setEntry(TEST_ALIAS_1, entry, null);
+ }
+
+ public void testKeyStore_Size_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ mKeyStore.setCertificateEntry(TEST_ALIAS_1, generateCertificate(FAKE_CA_1));
+
+ assertEquals("The keystore size should match expected", 1, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_1 });
+
+ mKeyStore.setCertificateEntry(TEST_ALIAS_2, generateCertificate(FAKE_CA_1));
+
+ assertEquals("The keystore size should match expected", 2, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
+
+ mKeyStore.setEntry(TEST_ALIAS_3, makeUserKey1(), null);
+
+ assertEquals("The keystore size should match expected", 3, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
+
+ mKeyStore.deleteEntry(TEST_ALIAS_1);
+
+ assertEquals("The keystore size should match expected", 2, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 });
+
+ mKeyStore.deleteEntry(TEST_ALIAS_3);
+
+ assertEquals("The keystore size should match expected", 1, mKeyStore.size());
+ assertAliases(new String[] { TEST_ALIAS_2 });
+ }
+
+ public void testKeyStore_Store_LoadStoreParam_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ try {
+ mKeyStore.store(null);
+ fail("Should throw UnsupportedOperationException when trying to store");
+ } catch (UnsupportedOperationException success) {
+ }
+ }
+
+ public void testKeyStore_Load_InputStreamSupplied_Unencrypted_Failure() throws Exception {
+ byte[] buf = "FAKE KEYSTORE".getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream(buf);
+
+ try {
+ mKeyStore.load(is, null);
+ fail("Should throw IllegalArgumentException when InputStream is supplied");
+ } catch (IllegalArgumentException success) {
+ }
+ }
+
+ public void testKeyStore_Load_PasswordSupplied_Unencrypted_Failure() throws Exception {
+ try {
+ mKeyStore.load(null, "password".toCharArray());
+ fail("Should throw IllegalArgumentException when password is supplied");
+ } catch (IllegalArgumentException success) {
+ }
+ }
+
+ public void testKeyStore_Store_OutputStream_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ OutputStream sink = new ByteArrayOutputStream();
+ try {
+ mKeyStore.store(sink, null);
+ fail("Should throw UnsupportedOperationException when trying to store");
+ } catch (UnsupportedOperationException success) {
+ }
+
+ try {
+ mKeyStore.store(sink, "blah".toCharArray());
+ fail("Should throw UnsupportedOperationException when trying to store");
+ } catch (UnsupportedOperationException success) {
+ }
+ }
+
+ public void testKeyStore_KeyOperations_Wrap_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, makeUserKey1(), null);
+
+ // Test key usage
+ Entry e = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull(e);
+ assertTrue(e instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry privEntry = (PrivateKeyEntry) e;
+ PrivateKey privKey = privEntry.getPrivateKey();
+ assertNotNull(privKey);
+
+ PublicKey pubKey = privEntry.getCertificate().getPublicKey();
+
+ Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ c.init(Cipher.WRAP_MODE, pubKey);
+
+ byte[] expectedKey = new byte[] {
+ 0x00, 0x05, (byte) 0xAA, (byte) 0x0A5, (byte) 0xFF, 0x55, 0x0A
+ };
+
+ SecretKey expectedSecret = new SecretKeySpec(expectedKey, "AES");
+
+ byte[] wrappedExpected = c.wrap(expectedSecret);
+
+ c.init(Cipher.UNWRAP_MODE, privKey);
+ SecretKey actualSecret = (SecretKey) c.unwrap(wrappedExpected, "AES", Cipher.SECRET_KEY);
+
+ assertEquals(Arrays.toString(expectedSecret.getEncoded()),
+ Arrays.toString(actualSecret.getEncoded()));
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorSpecTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorSpecTest.java
new file mode 100644
index 0000000..33c8955
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorSpecTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.security.KeyPairGeneratorSpec;
+import android.test.AndroidTestCase;
+
+import java.math.BigInteger;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+public class KeyPairGeneratorSpecTest extends AndroidTestCase {
+ private static final String TEST_ALIAS_1 = "test1";
+
+ private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
+
+ private static final long NOW_MILLIS = System.currentTimeMillis();
+
+ private static final BigInteger SERIAL_1 = BigInteger.ONE;
+
+ /* We have to round this off because X509v3 doesn't store milliseconds. */
+ private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
+
+ @SuppressWarnings("deprecation")
+ private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
+
+ public void testBuilder_Unencrypted_Success() throws Exception {
+ KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+
+ assertEquals("Context should be the one specified", getContext(), spec.getContext());
+
+ assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias());
+
+ assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN());
+
+ assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
+
+ assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
+
+ assertFalse("encryption flag should not be on", spec.isEncryptionRequired());
+ }
+
+ public void testBuilder_Encrypted_Success() throws Exception {
+ KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
+ .build();
+
+ assertEquals("Context should be the one specified", getContext(), spec.getContext());
+
+ assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias());
+
+ assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN());
+
+ assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
+
+ assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
+
+ assertTrue("encryption flag should be on", spec.isEncryptionRequired());
+ }
+
+ public void testBuilder_NullContext_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(null);
+ fail("Should throw NullPointerException when context is null");
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testBuilder_MissingAlias_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+ fail("Should throw IllegalArgumentException when alias is missing");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testBuilder_MissingSubjectDN_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSerialNumber(SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+ fail("Should throw IllegalArgumentException when subject is missing");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testBuilder_MissingSerialNumber_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+ fail("Should throw IllegalArgumentException when serialNumber is missing");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testBuilder_MissingStartDate_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(SERIAL_1)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+ fail("Should throw IllegalArgumentException when startDate is missing");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testBuilder_MissingEndDate_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(SERIAL_1)
+ .setStartDate(NOW)
+ .build();
+ fail("Should throw IllegalArgumentException when endDate is missing");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testBuilder_EndBeforeStart_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(SERIAL_1)
+ .setStartDate(NOW_PLUS_10_YEARS)
+ .setEndDate(NOW)
+ .build();
+ fail("Should throw IllegalArgumentException when end is before start");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
new file mode 100644
index 0000000..db6ce30
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.annotation.TargetApi;
+import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.os.Environment;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import com.android.cts.media.R;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Test for the integration of MediaMuxer and MediaCodec's encoder.
+ *
+ * <p>It uses MediaExtractor to get frames from a test stream, decodes them to a surface, uses a
+ * shader to edit them, encodes them from the resulting surface, and then uses MediaMuxer to write
+ * them into a file.
+ *
+ * <p>It does not currently check whether the result file is correct, but makes sure that nothing
+ * fails along the way.
+ *
+ * <p>It also tests the way the codec config buffers need to be passed from the MediaCodec to the
+ * MediaMuxer.
+ */
+@TargetApi(18)
+public class ExtractDecodeEditEncodeMuxTest extends AndroidTestCase {
+
+ private static final String TAG = ExtractDecodeEditEncodeMuxTest.class.getSimpleName();
+ private static final boolean VERBOSE = false; // lots of logging
+
+ /** How long to wait for the next buffer to become available. */
+ private static final int TIMEOUT_USEC = 10000;
+
+ /** Where to output the test files. */
+ private static final File OUTPUT_FILENAME_DIR = Environment.getExternalStorageDirectory();
+
+ // parameters for the video encoder
+ private static final String OUTPUT_VIDEO_MIME_TYPE = "video/avc"; // H.264 Advanced Video Coding
+ private static final int OUTPUT_VIDEO_BIT_RATE = 2000000; // 2Mbps
+ private static final int OUTPUT_VIDEO_FRAME_RATE = 15; // 15fps
+ private static final int OUTPUT_VIDEO_IFRAME_INTERVAL = 10; // 10 seconds between I-frames
+ private static final int OUTPUT_VIDEO_COLOR_FORMAT =
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
+
+ // parameters for the audio encoder
+ private static final String OUTPUT_AUDIO_MIME_TYPE = "audio/mp4a-latm"; // Advanced Audio Coding
+ private static final int OUTPUT_AUDIO_CHANNEL_COUNT = 2; // Must match the input stream.
+ private static final int OUTPUT_AUDIO_BIT_RATE = 128 * 1024;
+ private static final int OUTPUT_AUDIO_AAC_PROFILE =
+ MediaCodecInfo.CodecProfileLevel.AACObjectHE;
+ private static final int OUTPUT_AUDIO_SAMPLE_RATE_HZ = 44100; // Must match the input stream.
+
+ /**
+ * Used for editing the frames.
+ *
+ * <p>Swaps green and blue channels by storing an RBGA color in an RGBA buffer.
+ */
+ private static final String FRAGMENT_SHADER =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform samplerExternalOES sTexture;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(sTexture, vTextureCoord).rbga;\n" +
+ "}\n";
+
+ /** Whether to copy the video from the test video. */
+ private boolean mCopyVideo;
+ /** Whether to copy the audio from the test video. */
+ private boolean mCopyAudio;
+ /** Width of the output frames. */
+ private int mWidth = -1;
+ /** Height of the output frames. */
+ private int mHeight = -1;
+
+ /** The raw resource used as the input file. */
+ private int mSourceResId;
+
+ /** The destination file for the encoded output. */
+ private String mOutputFile;
+
+ public void testExtractDecodeEditEncodeMuxQCIF() throws Throwable {
+ setSize(176, 144);
+ setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
+ setCopyVideo();
+ TestWrapper.runTest(this);
+ }
+
+ public void testExtractDecodeEditEncodeMuxQVGA() throws Throwable {
+ setSize(320, 240);
+ setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
+ setCopyVideo();
+ TestWrapper.runTest(this);
+ }
+
+ public void testExtractDecodeEditEncodeMux720p() throws Throwable {
+ setSize(1280, 720);
+ setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
+ setCopyVideo();
+ TestWrapper.runTest(this);
+ }
+
+ public void testExtractDecodeEditEncodeMuxAudio() throws Throwable {
+ setSize(1280, 720);
+ setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
+ setCopyAudio();
+ TestWrapper.runTest(this);
+ }
+
+ public void testExtractDecodeEditEncodeMuxAudioVideo() throws Throwable {
+ setSize(1280, 720);
+ setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
+ setCopyAudio();
+ setCopyVideo();
+ TestWrapper.runTest(this);
+ }
+
+ /** Wraps testExtractDecodeEditEncodeMux() */
+ private static class TestWrapper implements Runnable {
+ private Throwable mThrowable;
+ private ExtractDecodeEditEncodeMuxTest mTest;
+
+ private TestWrapper(ExtractDecodeEditEncodeMuxTest test) {
+ mTest = test;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mTest.extractDecodeEditEncodeMux();
+ } catch (Throwable th) {
+ mThrowable = th;
+ }
+ }
+
+ /**
+ * Entry point.
+ */
+ public static void runTest(ExtractDecodeEditEncodeMuxTest test) throws Throwable {
+ test.setOutputFile();
+ TestWrapper wrapper = new TestWrapper(test);
+ Thread th = new Thread(wrapper, "codec test");
+ th.start();
+ th.join();
+ if (wrapper.mThrowable != null) {
+ throw wrapper.mThrowable;
+ }
+ }
+ }
+
+ /**
+ * Sets the test to copy the video stream.
+ */
+ private void setCopyVideo() {
+ mCopyVideo = true;
+ }
+
+ /**
+ * Sets the test to copy the video stream.
+ */
+ private void setCopyAudio() {
+ mCopyAudio = true;
+ }
+
+ /**
+ * Sets the desired frame size.
+ */
+ private void setSize(int width, int height) {
+ if ((width % 16) != 0 || (height % 16) != 0) {
+ Log.w(TAG, "WARNING: width or height not multiple of 16");
+ }
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * Sets the raw resource used as the source video.
+ */
+ private void setSource(int resId) {
+ mSourceResId = resId;
+ }
+
+ /**
+ * Sets the name of the output file based on the other parameters.
+ *
+ * <p>Must be called after {@link #setSize(int, int)} and {@link #setSource(int)}.
+ */
+ private void setOutputFile() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(OUTPUT_FILENAME_DIR.getAbsolutePath());
+ sb.append("/cts-media-");
+ sb.append(getClass().getSimpleName());
+ assertTrue("should have called setSource() first", mSourceResId != -1);
+ sb.append('-');
+ sb.append(mSourceResId);
+ if (mCopyVideo) {
+ assertTrue("should have called setSize() first", mWidth != -1);
+ assertTrue("should have called setSize() first", mHeight != -1);
+ sb.append('-');
+ sb.append("video");
+ sb.append('-');
+ sb.append(mWidth);
+ sb.append('x');
+ sb.append(mHeight);
+ }
+ if (mCopyAudio) {
+ sb.append('-');
+ sb.append("audio");
+ }
+ sb.append(".mp4");
+ mOutputFile = sb.toString();
+ }
+
+ /**
+ * Tests encoding and subsequently decoding video from frames generated into a buffer.
+ * <p>
+ * We encode several frames of a video test pattern using MediaCodec, then decode the output
+ * with MediaCodec and do some simple checks.
+ */
+ private void extractDecodeEditEncodeMux() throws Exception {
+ // Exception that may be thrown during release.
+ Exception exception = null;
+
+ MediaCodecInfo videoCodecInfo = selectCodec(OUTPUT_VIDEO_MIME_TYPE);
+ if (videoCodecInfo == null) {
+ // Don't fail CTS if they don't have an AVC codec (not here, anyway).
+ Log.e(TAG, "Unable to find an appropriate codec for " + OUTPUT_VIDEO_MIME_TYPE);
+ return;
+ }
+ if (VERBOSE) Log.d(TAG, "video found codec: " + videoCodecInfo.getName());
+
+ MediaCodecInfo audioCodecInfo = selectCodec(OUTPUT_AUDIO_MIME_TYPE);
+ if (audioCodecInfo == null) {
+ // Don't fail CTS if they don't have an AAC codec (not here, anyway).
+ Log.e(TAG, "Unable to find an appropriate codec for " + OUTPUT_AUDIO_MIME_TYPE);
+ return;
+ }
+ if (VERBOSE) Log.d(TAG, "audio found codec: " + audioCodecInfo.getName());
+
+ MediaExtractor videoExtractor = null;
+ MediaExtractor audioExtractor = null;
+ OutputSurface outputSurface = null;
+ MediaCodec videoDecoder = null;
+ MediaCodec audioDecoder = null;
+ MediaCodec videoEncoder = null;
+ MediaCodec audioEncoder = null;
+ MediaMuxer muxer = null;
+
+ InputSurface inputSurface = null;
+
+ try {
+ if (mCopyVideo) {
+ videoExtractor = createExtractor();
+ int videoInputTrack = getAndSelectVideoTrackIndex(videoExtractor);
+ assertTrue("missing video track in test video", videoInputTrack != -1);
+ MediaFormat inputFormat = videoExtractor.getTrackFormat(videoInputTrack);
+
+ // We avoid the device-specific limitations on width and height by using values
+ // that are multiples of 16, which all tested devices seem to be able to handle.
+ MediaFormat outputVideoFormat =
+ MediaFormat.createVideoFormat(OUTPUT_VIDEO_MIME_TYPE, mWidth, mHeight);
+
+ // Set some properties. Failing to specify some of these can cause the MediaCodec
+ // configure() call to throw an unhelpful exception.
+ outputVideoFormat.setInteger(
+ MediaFormat.KEY_COLOR_FORMAT, OUTPUT_VIDEO_COLOR_FORMAT);
+ outputVideoFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_VIDEO_BIT_RATE);
+ outputVideoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, OUTPUT_VIDEO_FRAME_RATE);
+ outputVideoFormat.setInteger(
+ MediaFormat.KEY_I_FRAME_INTERVAL, OUTPUT_VIDEO_IFRAME_INTERVAL);
+ if (VERBOSE) Log.d(TAG, "video format: " + outputVideoFormat);
+
+ // Create a MediaCodec for the desired codec, then configure it as an encoder with
+ // our desired properties. Request a Surface to use for input.
+ AtomicReference<Surface> inputSurfaceReference = new AtomicReference<Surface>();
+ videoEncoder = createVideoEncoder(
+ videoCodecInfo, outputVideoFormat, inputSurfaceReference);
+ inputSurface = new InputSurface(inputSurfaceReference.get());
+ inputSurface.makeCurrent();
+ // Create a MediaCodec for the decoder, based on the extractor's format.
+ outputSurface = new OutputSurface();
+ outputSurface.changeFragmentShader(FRAGMENT_SHADER);
+ videoDecoder = createVideoDecoder(inputFormat, outputSurface.getSurface());
+ }
+
+ if (mCopyAudio) {
+ audioExtractor = createExtractor();
+ int audioInputTrack = getAndSelectAudioTrackIndex(audioExtractor);
+ assertTrue("missing audio track in test video", audioInputTrack != -1);
+ MediaFormat inputFormat = audioExtractor.getTrackFormat(audioInputTrack);
+
+ MediaFormat outputAudioFormat =
+ MediaFormat.createAudioFormat(
+ OUTPUT_AUDIO_MIME_TYPE, OUTPUT_AUDIO_SAMPLE_RATE_HZ,
+ OUTPUT_AUDIO_CHANNEL_COUNT);
+ outputAudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_AUDIO_BIT_RATE);
+ outputAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, OUTPUT_AUDIO_AAC_PROFILE);
+
+ // Create a MediaCodec for the desired codec, then configure it as an encoder with
+ // our desired properties. Request a Surface to use for input.
+ audioEncoder = createAudioEncoder(audioCodecInfo, outputAudioFormat);
+ // Create a MediaCodec for the decoder, based on the extractor's format.
+ audioDecoder = createAudioDecoder(inputFormat);
+ }
+
+ // Creates a muxer but do not start or add tracks just yet.
+ muxer = createMuxer();
+
+ doExtractDecodeEditEncodeMux(
+ videoExtractor,
+ audioExtractor,
+ videoDecoder,
+ videoEncoder,
+ audioDecoder,
+ audioEncoder,
+ muxer,
+ inputSurface,
+ outputSurface);
+ } finally {
+ if (VERBOSE) Log.d(TAG, "releasing extractor, decoder, encoder, and muxer");
+ // Try to release everything we acquired, even if one of the releases fails, in which
+ // case we save the first exception we got and re-throw at the end (unless something
+ // other exception has already been thrown). This guarantees the first exception thrown
+ // is reported as the cause of the error, everything is (attempted) to be released, and
+ // all other exceptions appear in the logs.
+ try {
+ if (videoExtractor != null) {
+ videoExtractor.release();
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "error while releasing videoExtractor", e);
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ try {
+ if (audioExtractor != null) {
+ audioExtractor.release();
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "error while releasing audioExtractor", e);
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ try {
+ if (videoDecoder != null) {
+ videoDecoder.stop();
+ videoDecoder.release();
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "error while releasing videoDecoder", e);
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ try {
+ if (outputSurface != null) {
+ outputSurface.release();
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "error while releasing outputSurface", e);
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ try {
+ if (videoEncoder != null) {
+ videoEncoder.stop();
+ videoEncoder.release();
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "error while releasing videoEncoder", e);
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ try {
+ if (audioDecoder != null) {
+ audioDecoder.stop();
+ audioDecoder.release();
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "error while releasing audioDecoder", e);
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ try {
+ if (audioEncoder != null) {
+ audioEncoder.stop();
+ audioEncoder.release();
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "error while releasing audioEncoder", e);
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ try {
+ if (muxer != null) {
+ muxer.stop();
+ muxer.release();
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "error while releasing muxer", e);
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ try {
+ if (inputSurface != null) {
+ inputSurface.release();
+ }
+ } catch(Exception e) {
+ Log.e(TAG, "error while releasing inputSurface", e);
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ }
+
+ /**
+ * Creates an extractor that reads its frames from {@link #mSourceResId}.
+ */
+ private MediaExtractor createExtractor() throws IOException {
+ MediaExtractor extractor;
+ AssetFileDescriptor srcFd = getContext().getResources().openRawResourceFd(mSourceResId);
+ extractor = new MediaExtractor();
+ extractor.setDataSource(srcFd.getFileDescriptor(), srcFd.getStartOffset(),
+ srcFd.getLength());
+ return extractor;
+ }
+
+ /**
+ * Creates a decoder for the given format, which outputs to the given surface.
+ *
+ * @param inputFormat the format of the stream to decode
+ * @param surface into which to decode the frames
+ */
+ private MediaCodec createVideoDecoder(MediaFormat inputFormat, Surface surface) {
+ MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat));
+ decoder.configure(inputFormat, surface, null, 0);
+ decoder.start();
+ return decoder;
+ }
+
+ /**
+ * Creates an encoder for the given format using the specified codec, taking input from a
+ * surface.
+ *
+ * <p>The surface to use as input is stored in the given reference.
+ *
+ * @param codecInfo of the codec to use
+ * @param format of the stream to be produced
+ * @param surfaceReference to store the surface to use as input
+ */
+ private MediaCodec createVideoEncoder(
+ MediaCodecInfo codecInfo,
+ MediaFormat format,
+ AtomicReference<Surface> surfaceReference) {
+ MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName());
+ encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ // Must be called before start() is.
+ surfaceReference.set(encoder.createInputSurface());
+ encoder.start();
+ return encoder;
+ }
+
+ /**
+ * Creates a decoder for the given format.
+ *
+ * @param inputFormat the format of the stream to decode
+ */
+ private MediaCodec createAudioDecoder(MediaFormat inputFormat) {
+ MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat));
+ decoder.configure(inputFormat, null, null, 0);
+ decoder.start();
+ return decoder;
+ }
+
+ /**
+ * Creates an encoder for the given format using the specified codec.
+ *
+ * @param codecInfo of the codec to use
+ * @param format of the stream to be produced
+ */
+ private MediaCodec createAudioEncoder(MediaCodecInfo codecInfo, MediaFormat format) {
+ MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName());
+ encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ encoder.start();
+ return encoder;
+ }
+
+ /**
+ * Creates a muxer to write the encoded frames.
+ *
+ * <p>The muxer is not started as it needs to be started only after all streams have been added.
+ */
+ private MediaMuxer createMuxer() throws IOException {
+ return new MediaMuxer(mOutputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+ }
+
+ private int getAndSelectVideoTrackIndex(MediaExtractor extractor) {
+ for (int index = 0; index < extractor.getTrackCount(); ++index) {
+ if (VERBOSE) {
+ Log.d(TAG, "format for track " + index + " is "
+ + getMimeTypeFor(extractor.getTrackFormat(index)));
+ }
+ if (isVideoFormat(extractor.getTrackFormat(index))) {
+ extractor.selectTrack(index);
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ private int getAndSelectAudioTrackIndex(MediaExtractor extractor) {
+ for (int index = 0; index < extractor.getTrackCount(); ++index) {
+ if (VERBOSE) {
+ Log.d(TAG, "format for track " + index + " is "
+ + getMimeTypeFor(extractor.getTrackFormat(index)));
+ }
+ if (isAudioFormat(extractor.getTrackFormat(index))) {
+ extractor.selectTrack(index);
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Does the actual work for extracting, decoding, encoding and muxing.
+ */
+ private void doExtractDecodeEditEncodeMux(
+ MediaExtractor videoExtractor,
+ MediaExtractor audioExtractor,
+ MediaCodec videoDecoder,
+ MediaCodec videoEncoder,
+ MediaCodec audioDecoder,
+ MediaCodec audioEncoder,
+ MediaMuxer muxer,
+ InputSurface inputSurface,
+ OutputSurface outputSurface) {
+ ByteBuffer[] videoDecoderInputBuffers = null;
+ ByteBuffer[] videoDecoderOutputBuffers = null;
+ ByteBuffer[] videoEncoderOutputBuffers = null;
+ MediaCodec.BufferInfo videoDecoderOutputBufferInfo = null;
+ MediaCodec.BufferInfo videoEncoderOutputBufferInfo = null;
+ if (mCopyVideo) {
+ videoDecoderInputBuffers = videoDecoder.getInputBuffers();
+ videoDecoderOutputBuffers = videoDecoder.getOutputBuffers();
+ videoEncoderOutputBuffers = videoEncoder.getOutputBuffers();
+ videoDecoderOutputBufferInfo = new MediaCodec.BufferInfo();
+ videoEncoderOutputBufferInfo = new MediaCodec.BufferInfo();
+ }
+ ByteBuffer[] audioDecoderInputBuffers = null;
+ ByteBuffer[] audioDecoderOutputBuffers = null;
+ ByteBuffer[] audioEncoderInputBuffers = null;
+ ByteBuffer[] audioEncoderOutputBuffers = null;
+ MediaCodec.BufferInfo audioDecoderOutputBufferInfo = null;
+ MediaCodec.BufferInfo audioEncoderOutputBufferInfo = null;
+ if (mCopyAudio) {
+ audioDecoderInputBuffers = audioDecoder.getInputBuffers();
+ audioDecoderOutputBuffers = audioDecoder.getOutputBuffers();
+ audioEncoderInputBuffers = audioEncoder.getInputBuffers();
+ audioEncoderOutputBuffers = audioEncoder.getOutputBuffers();
+ audioDecoderOutputBufferInfo = new MediaCodec.BufferInfo();
+ audioEncoderOutputBufferInfo = new MediaCodec.BufferInfo();
+ }
+ // We will get these from the decoders when notified of a format change.
+ MediaFormat decoderOutputVideoFormat = null;
+ MediaFormat decoderOutputAudioFormat = null;
+ // We will get these from the encoders when notified of a format change.
+ MediaFormat encoderOutputVideoFormat = null;
+ MediaFormat encoderOutputAudioFormat = null;
+ // We will determine these once we have the output format.
+ int outputVideoTrack = -1;
+ int outputAudioTrack = -1;
+ // Whether things are done on the video side.
+ boolean videoExtractorDone = false;
+ boolean videoDecoderDone = false;
+ boolean videoEncoderDone = false;
+ // Whether things are done on the audio side.
+ boolean audioExtractorDone = false;
+ boolean audioDecoderDone = false;
+ boolean audioEncoderDone = false;
+ // The audio decoder output buffer to process, -1 if none.
+ int pendingAudioDecoderOutputBufferIndex = -1;
+
+ boolean muxing = false;
+
+ int videoExtractedFrameCount = 0;
+ int videoDecodedFrameCount = 0;
+ int videoEncodedFrameCount = 0;
+
+ int audioExtractedFrameCount = 0;
+ int audioDecodedFrameCount = 0;
+ int audioEncodedFrameCount = 0;
+
+ while ((mCopyVideo && !videoEncoderDone) || (mCopyAudio && !audioEncoderDone)) {
+ if (VERBOSE) {
+ Log.d(TAG, String.format(
+ "loop: "
+
+ + "V(%b){"
+ + "extracted:%d(done:%b) "
+ + "decoded:%d(done:%b) "
+ + "encoded:%d(done:%b)} "
+
+ + "A(%b){"
+ + "extracted:%d(done:%b) "
+ + "decoded:%d(done:%b) "
+ + "encoded:%d(done:%b) "
+ + "pending:%d} "
+
+ + "muxing:%b(V:%d,A:%d)",
+
+ mCopyVideo,
+ videoExtractedFrameCount, videoExtractorDone,
+ videoDecodedFrameCount, videoDecoderDone,
+ videoEncodedFrameCount, videoEncoderDone,
+
+ mCopyAudio,
+ audioExtractedFrameCount, audioExtractorDone,
+ audioDecodedFrameCount, audioDecoderDone,
+ audioEncodedFrameCount, audioEncoderDone,
+ pendingAudioDecoderOutputBufferIndex,
+
+ muxing, outputVideoTrack, outputAudioTrack));
+ }
+
+ // Extract video from file and feed to decoder.
+ // Do not extract video if we have determined the output format but we are not yet
+ // ready to mux the frames.
+ while (mCopyVideo && !videoExtractorDone
+ && (encoderOutputVideoFormat == null || muxing)) {
+ int decoderInputBufferIndex = videoDecoder.dequeueInputBuffer(TIMEOUT_USEC);
+ if (decoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ if (VERBOSE) Log.d(TAG, "no video decoder input buffer");
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "video decoder: returned input buffer: " + decoderInputBufferIndex);
+ }
+ ByteBuffer decoderInputBuffer = videoDecoderInputBuffers[decoderInputBufferIndex];
+ int size = videoExtractor.readSampleData(decoderInputBuffer, 0);
+ long presentationTime = videoExtractor.getSampleTime();
+ if (VERBOSE) {
+ Log.d(TAG, "video extractor: returned buffer of size " + size);
+ Log.d(TAG, "video extractor: returned buffer for time " + presentationTime);
+ }
+ if (size >= 0) {
+ videoDecoder.queueInputBuffer(
+ decoderInputBufferIndex,
+ 0,
+ size,
+ presentationTime,
+ videoExtractor.getSampleFlags());
+ }
+ videoExtractorDone = !videoExtractor.advance();
+ if (videoExtractorDone) {
+ if (VERBOSE) Log.d(TAG, "video extractor: EOS");
+ videoDecoder.queueInputBuffer(
+ decoderInputBufferIndex,
+ 0,
+ 0,
+ 0,
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+ }
+ videoExtractedFrameCount++;
+ // We extracted a frame, let's try something else next.
+ break;
+ }
+
+ // Extract audio from file and feed to decoder.
+ // Do not extract audio if we have determined the output format but we are not yet
+ // ready to mux the frames.
+ while (mCopyAudio && !audioExtractorDone
+ && (encoderOutputAudioFormat == null || muxing)) {
+ int decoderInputBufferIndex = audioDecoder.dequeueInputBuffer(TIMEOUT_USEC);
+ if (decoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ if (VERBOSE) Log.d(TAG, "no audio decoder input buffer");
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "audio decoder: returned input buffer: " + decoderInputBufferIndex);
+ }
+ ByteBuffer decoderInputBuffer = audioDecoderInputBuffers[decoderInputBufferIndex];
+ int size = audioExtractor.readSampleData(decoderInputBuffer, 0);
+ long presentationTime = audioExtractor.getSampleTime();
+ if (VERBOSE) {
+ Log.d(TAG, "audio extractor: returned buffer of size " + size);
+ Log.d(TAG, "audio extractor: returned buffer for time " + presentationTime);
+ }
+ if (size >= 0) {
+ audioDecoder.queueInputBuffer(
+ decoderInputBufferIndex,
+ 0,
+ size,
+ presentationTime,
+ audioExtractor.getSampleFlags());
+ }
+ audioExtractorDone = !audioExtractor.advance();
+ if (audioExtractorDone) {
+ if (VERBOSE) Log.d(TAG, "audio extractor: EOS");
+ audioDecoder.queueInputBuffer(
+ decoderInputBufferIndex,
+ 0,
+ 0,
+ 0,
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+ }
+ audioExtractedFrameCount++;
+ // We extracted a frame, let's try something else next.
+ break;
+ }
+
+ // Poll output frames from the video decoder and feed the encoder.
+ while (mCopyVideo && !videoDecoderDone
+ && (encoderOutputVideoFormat == null || muxing)) {
+ int decoderOutputBufferIndex =
+ videoDecoder.dequeueOutputBuffer(
+ videoDecoderOutputBufferInfo, TIMEOUT_USEC);
+ if (decoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ if (VERBOSE) Log.d(TAG, "no video decoder output buffer");
+ break;
+ }
+ if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ if (VERBOSE) Log.d(TAG, "video decoder: output buffers changed");
+ videoDecoderOutputBuffers = videoDecoder.getOutputBuffers();
+ break;
+ }
+ if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ decoderOutputVideoFormat = videoDecoder.getOutputFormat();
+ if (VERBOSE) {
+ Log.d(TAG, "video decoder: output format changed: "
+ + decoderOutputVideoFormat);
+ }
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "video decoder: returned output buffer: "
+ + decoderOutputBufferIndex);
+ Log.d(TAG, "video decoder: returned buffer of size "
+ + videoDecoderOutputBufferInfo.size);
+ }
+ ByteBuffer decoderOutputBuffer =
+ videoDecoderOutputBuffers[decoderOutputBufferIndex];
+ if ((videoDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)
+ != 0) {
+ if (VERBOSE) Log.d(TAG, "video decoder: codec config buffer");
+ videoDecoder.releaseOutputBuffer(decoderOutputBufferIndex, false);
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "video decoder: returned buffer for time "
+ + videoDecoderOutputBufferInfo.presentationTimeUs);
+ }
+ boolean render = videoDecoderOutputBufferInfo.size != 0;
+ videoDecoder.releaseOutputBuffer(decoderOutputBufferIndex, render);
+ if (render) {
+ if (VERBOSE) Log.d(TAG, "output surface: await new image");
+ outputSurface.awaitNewImage();
+ // Edit the frame and send it to the encoder.
+ if (VERBOSE) Log.d(TAG, "output surface: draw image");
+ outputSurface.drawImage();
+ inputSurface.setPresentationTime(
+ videoDecoderOutputBufferInfo.presentationTimeUs);
+ if (VERBOSE) Log.d(TAG, "input surface: swap buffers");
+ inputSurface.swapBuffers();
+ if (VERBOSE) Log.d(TAG, "video encoder: notified of new frame");
+ }
+ if ((videoDecoderOutputBufferInfo.flags
+ & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ if (VERBOSE) Log.d(TAG, "video decoder: EOS");
+ videoDecoderDone = true;
+ videoEncoder.signalEndOfInputStream();
+ }
+ videoDecodedFrameCount++;
+ // We extracted a pending frame, let's try something else next.
+ break;
+ }
+
+ // Poll output frames from the audio decoder.
+ // Do not poll if we already have a pending buffer to feed to the encoder.
+ while (mCopyAudio && !audioDecoderDone && pendingAudioDecoderOutputBufferIndex == -1
+ && (encoderOutputAudioFormat == null || muxing)) {
+ int decoderOutputBufferIndex =
+ audioDecoder.dequeueOutputBuffer(
+ audioDecoderOutputBufferInfo, TIMEOUT_USEC);
+ if (decoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ if (VERBOSE) Log.d(TAG, "no audio decoder output buffer");
+ break;
+ }
+ if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ if (VERBOSE) Log.d(TAG, "audio decoder: output buffers changed");
+ audioDecoderOutputBuffers = audioDecoder.getOutputBuffers();
+ break;
+ }
+ if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ decoderOutputAudioFormat = audioDecoder.getOutputFormat();
+ if (VERBOSE) {
+ Log.d(TAG, "audio decoder: output format changed: "
+ + decoderOutputAudioFormat);
+ }
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "audio decoder: returned output buffer: "
+ + decoderOutputBufferIndex);
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "audio decoder: returned buffer of size "
+ + audioDecoderOutputBufferInfo.size);
+ }
+ ByteBuffer decoderOutputBuffer =
+ audioDecoderOutputBuffers[decoderOutputBufferIndex];
+ if ((audioDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)
+ != 0) {
+ if (VERBOSE) Log.d(TAG, "audio decoder: codec config buffer");
+ audioDecoder.releaseOutputBuffer(decoderOutputBufferIndex, false);
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "audio decoder: returned buffer for time "
+ + audioDecoderOutputBufferInfo.presentationTimeUs);
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "audio decoder: output buffer is now pending: "
+ + pendingAudioDecoderOutputBufferIndex);
+ }
+ pendingAudioDecoderOutputBufferIndex = decoderOutputBufferIndex;
+ audioDecodedFrameCount++;
+ // We extracted a pending frame, let's try something else next.
+ break;
+ }
+
+ // Feed the pending decoded audio buffer to the audio encoder.
+ while (mCopyAudio && pendingAudioDecoderOutputBufferIndex != -1) {
+ if (VERBOSE) {
+ Log.d(TAG, "audio decoder: attempting to process pending buffer: "
+ + pendingAudioDecoderOutputBufferIndex);
+ }
+ int encoderInputBufferIndex = audioEncoder.dequeueInputBuffer(TIMEOUT_USEC);
+ if (encoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ if (VERBOSE) Log.d(TAG, "no audio encoder input buffer");
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "audio encoder: returned input buffer: " + encoderInputBufferIndex);
+ }
+ ByteBuffer encoderInputBuffer = audioEncoderInputBuffers[encoderInputBufferIndex];
+ int size = audioDecoderOutputBufferInfo.size;
+ long presentationTime = audioDecoderOutputBufferInfo.presentationTimeUs;
+ if (VERBOSE) {
+ Log.d(TAG, "audio decoder: processing pending buffer: "
+ + pendingAudioDecoderOutputBufferIndex);
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "audio decoder: pending buffer of size " + size);
+ Log.d(TAG, "audio decoder: pending buffer for time " + presentationTime);
+ }
+ if (size >= 0) {
+ ByteBuffer decoderOutputBuffer =
+ audioDecoderOutputBuffers[pendingAudioDecoderOutputBufferIndex]
+ .duplicate();
+ decoderOutputBuffer.position(audioDecoderOutputBufferInfo.offset);
+ decoderOutputBuffer.limit(audioDecoderOutputBufferInfo.offset + size);
+ encoderInputBuffer.position(0);
+ encoderInputBuffer.put(decoderOutputBuffer);
+
+ audioEncoder.queueInputBuffer(
+ encoderInputBufferIndex,
+ 0,
+ size,
+ presentationTime,
+ audioDecoderOutputBufferInfo.flags);
+ }
+ audioDecoder.releaseOutputBuffer(pendingAudioDecoderOutputBufferIndex, false);
+ pendingAudioDecoderOutputBufferIndex = -1;
+ if ((audioDecoderOutputBufferInfo.flags
+ & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ if (VERBOSE) Log.d(TAG, "audio decoder: EOS");
+ audioDecoderDone = true;
+ }
+ // We enqueued a pending frame, let's try something else next.
+ break;
+ }
+
+ // Poll frames from the video encoder and send them to the muxer.
+ while (mCopyVideo && !videoEncoderDone
+ && (encoderOutputVideoFormat == null || muxing)) {
+ int encoderOutputBufferIndex = videoEncoder.dequeueOutputBuffer(
+ videoEncoderOutputBufferInfo, TIMEOUT_USEC);
+ if (encoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ if (VERBOSE) Log.d(TAG, "no video encoder output buffer");
+ break;
+ }
+ if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ if (VERBOSE) Log.d(TAG, "video encoder: output buffers changed");
+ videoEncoderOutputBuffers = videoEncoder.getOutputBuffers();
+ break;
+ }
+ if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ if (VERBOSE) Log.d(TAG, "video encoder: output format changed");
+ if (outputVideoTrack >= 0) {
+ fail("video encoder changed its output format again?");
+ }
+ encoderOutputVideoFormat = videoEncoder.getOutputFormat();
+ break;
+ }
+ assertTrue("should have added track before processing output", muxing);
+ if (VERBOSE) {
+ Log.d(TAG, "video encoder: returned output buffer: "
+ + encoderOutputBufferIndex);
+ Log.d(TAG, "video encoder: returned buffer of size "
+ + videoEncoderOutputBufferInfo.size);
+ }
+ ByteBuffer encoderOutputBuffer =
+ videoEncoderOutputBuffers[encoderOutputBufferIndex];
+ if ((videoEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)
+ != 0) {
+ if (VERBOSE) Log.d(TAG, "video encoder: codec config buffer");
+ // Simply ignore codec config buffers.
+ videoEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false);
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "video encoder: returned buffer for time "
+ + videoEncoderOutputBufferInfo.presentationTimeUs);
+ }
+ if (videoEncoderOutputBufferInfo.size != 0) {
+ muxer.writeSampleData(
+ outputVideoTrack, encoderOutputBuffer, videoEncoderOutputBufferInfo);
+ }
+ if ((videoEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM)
+ != 0) {
+ if (VERBOSE) Log.d(TAG, "video encoder: EOS");
+ videoEncoderDone = true;
+ }
+ videoEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false);
+ videoEncodedFrameCount++;
+ // We enqueued an encoded frame, let's try something else next.
+ break;
+ }
+
+ // Poll frames from the audio encoder and send them to the muxer.
+ while (mCopyAudio && !audioEncoderDone
+ && (encoderOutputAudioFormat == null || muxing)) {
+ int encoderOutputBufferIndex = audioEncoder.dequeueOutputBuffer(
+ audioEncoderOutputBufferInfo, TIMEOUT_USEC);
+ if (encoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ if (VERBOSE) Log.d(TAG, "no audio encoder output buffer");
+ break;
+ }
+ if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ if (VERBOSE) Log.d(TAG, "audio encoder: output buffers changed");
+ audioEncoderOutputBuffers = audioEncoder.getOutputBuffers();
+ break;
+ }
+ if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ if (VERBOSE) Log.d(TAG, "audio encoder: output format changed");
+ if (outputAudioTrack >= 0) {
+ fail("audio encoder changed its output format again?");
+ }
+
+ encoderOutputAudioFormat = audioEncoder.getOutputFormat();
+ break;
+ }
+ assertTrue("should have added track before processing output", muxing);
+ if (VERBOSE) {
+ Log.d(TAG, "audio encoder: returned output buffer: "
+ + encoderOutputBufferIndex);
+ Log.d(TAG, "audio encoder: returned buffer of size "
+ + audioEncoderOutputBufferInfo.size);
+ }
+ ByteBuffer encoderOutputBuffer =
+ audioEncoderOutputBuffers[encoderOutputBufferIndex];
+ if ((audioEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)
+ != 0) {
+ if (VERBOSE) Log.d(TAG, "audio encoder: codec config buffer");
+ // Simply ignore codec config buffers.
+ audioEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false);
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "audio encoder: returned buffer for time "
+ + audioEncoderOutputBufferInfo.presentationTimeUs);
+ }
+ if (audioEncoderOutputBufferInfo.size != 0) {
+ muxer.writeSampleData(
+ outputAudioTrack, encoderOutputBuffer, audioEncoderOutputBufferInfo);
+ }
+ if ((audioEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM)
+ != 0) {
+ if (VERBOSE) Log.d(TAG, "audio encoder: EOS");
+ audioEncoderDone = true;
+ }
+ audioEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false);
+ audioEncodedFrameCount++;
+ // We enqueued an encoded frame, let's try something else next.
+ break;
+ }
+
+ if (!muxing
+ && (!mCopyAudio || encoderOutputAudioFormat != null)
+ && (!mCopyVideo || encoderOutputVideoFormat != null)) {
+ if (mCopyVideo) {
+ Log.d(TAG, "muxer: adding video track.");
+ outputVideoTrack = muxer.addTrack(encoderOutputVideoFormat);
+ }
+ if (mCopyAudio) {
+ Log.d(TAG, "muxer: adding audio track.");
+ outputAudioTrack = muxer.addTrack(encoderOutputAudioFormat);
+ }
+ Log.d(TAG, "muxer: starting");
+ muxer.start();
+ muxing = true;
+ }
+ }
+
+ // Basic sanity checks.
+ if (mCopyVideo) {
+ assertEquals("encoded and decoded video frame counts should match",
+ videoDecodedFrameCount, videoEncodedFrameCount);
+ assertTrue("decoded frame count should be less than extracted frame count",
+ videoDecodedFrameCount <= videoExtractedFrameCount);
+ }
+ if (mCopyAudio) {
+ assertEquals("no frame should be pending", -1, pendingAudioDecoderOutputBufferIndex);
+ }
+
+ // TODO: Check the generated output file.
+ }
+
+ private static boolean isVideoFormat(MediaFormat format) {
+ return getMimeTypeFor(format).startsWith("video/");
+ }
+
+ private static boolean isAudioFormat(MediaFormat format) {
+ return getMimeTypeFor(format).startsWith("audio/");
+ }
+
+ private static String getMimeTypeFor(MediaFormat format) {
+ return format.getString(MediaFormat.KEY_MIME);
+ }
+
+ /**
+ * Returns the first codec capable of encoding the specified MIME type, or null if no match was
+ * found.
+ */
+ private static MediaCodecInfo selectCodec(String mimeType) {
+ int numCodecs = MediaCodecList.getCodecCount();
+ for (int i = 0; i < numCodecs; i++) {
+ MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+ if (!codecInfo.isEncoder()) {
+ continue;
+ }
+
+ String[] types = codecInfo.getSupportedTypes();
+ for (int j = 0; j < types.length; j++) {
+ if (types[j].equalsIgnoreCase(mimeType)) {
+ return codecInfo;
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp b/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp
index 63311eb..a1cd16f 100644
--- a/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp
+++ b/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp
@@ -68,7 +68,7 @@
return JNI_FALSE;
}
jboolean res = JNI_TRUE;
- EGLint result = mEglClientWaitSyncKHR(dpy, sync, 0, waitTimeInNs);
+ EGLint result = mEglClientWaitSyncKHR(dpy, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, waitTimeInNs);
if (result == EGL_FALSE) {
ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
res = JNI_FALSE;
diff --git a/tests/tests/os/src/android/os/cts/SecurityFeaturesTest.java b/tests/tests/os/src/android/os/cts/SecurityFeaturesTest.java
new file mode 100644
index 0000000..6a02974
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/SecurityFeaturesTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.cts;
+
+import junit.framework.TestCase;
+
+public class SecurityFeaturesTest extends TestCase {
+
+ public void testNoNewPrivs() {
+ int newPrivs = OSFeatures.getNoNewPrivs();
+ // if newPrivs == -1, then old kernel with no PR_SET_NO_NEW_PRIVS (acceptable)
+ // if newPrivs == 0, then new kernel with PR_SET_NO_NEW_PRIVS disabled (BAD)
+ // if newPrivs == 1, then new kernel with PR_SET_NO_NEW_PRIVS enabled (GOOD)
+ assertTrue(newPrivs != 0);
+ }
+
+ /**
+ * Iterate over all possible capabilities, testing to make sure each capability
+ * has been removed from the app's capability bounding set.
+ */
+ public void testPrCapbsetEmpty() {
+ int i = 0;
+ while (true) {
+ int result = OSFeatures.prctlCapBsetRead(i);
+ if (result == -1) {
+ // The kernel has told us that the capability we're inquiring about
+ // doesn't exist. Capabilities are assigned sequentially and
+ // and monotonically increase with each kernel release, so if we
+ // see -1, we know we've examined every capability the kernel
+ // knows about.
+ break;
+ }
+ assertEquals("capability " + i + " is still in the bounding set",
+ 0, result);
+ i++;
+ }
+ }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 7b6a932..3abcbb6 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -686,6 +686,12 @@
getAllInsecureDevicesInDirAndSubdir(File dir) throws Exception {
assertTrue(dir.isDirectory());
Set<File> retval = new HashSet<File>();
+
+ if (isSymbolicLink(dir)) {
+ // don't examine symbolic links.
+ return retval;
+ }
+
File[] subDirectories = dir.listFiles(new FileFilter() {
@Override public boolean accept(File pathname) {
return pathname.isDirectory();
diff --git a/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java b/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java
index 3736b29..5ae692b 100644
--- a/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java
@@ -300,6 +300,11 @@
public boolean canSeekForward() {
return true;
}
+
+ @Override
+ public int getAudioSessionId() {
+ return 0;
+ }
}
private static class MockOnClickListener implements OnClickListener {
diff --git a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
index 830ea60..80680c0 100644
--- a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
+++ b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
@@ -770,6 +770,20 @@
}
/**
+ * Verify the UiSelector property resourceIdMatches
+ *
+ * @throws UiObjectNotFoundException
+ * @since API Level 18
+ */
+ public void testSelectorResourceIdMatches() throws UiObjectNotFoundException {
+ openTest("Test 2");
+ new UiObject(new UiSelector().resourceIdMatches("(?i).*button.*").instance(2)).click();
+ verifyDialogActionResults("Button 3");
+ new UiObject(new UiSelector().resourceIdMatches("(?i).*button1.*")).click();
+ verifyDialogActionResults("Button 1");
+ }
+
+ /**
* Performs a pinch out from the center of a view to its edges and listens to
* the motion events to make sure the starting and ending points of both pointers
* are correct.