Merge "Make AlmostFullTest no longer overfill the disk." into nougat-mr1-cts-dev
diff --git a/OldCtsTestCaseList.mk b/OldCtsTestCaseList.mk
index c54d283..a1a7d5a16 100644
--- a/OldCtsTestCaseList.mk
+++ b/OldCtsTestCaseList.mk
@@ -291,6 +291,7 @@
cts_native_tests := \
CtsNativeMediaSlTestCases \
CtsNativeMediaXaTestCases \
+ CtsNativeNetTestCases \
ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
cts_native_tests += CtsBionicTestCases
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index e5fbba5..00fb1d6 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -86,8 +86,8 @@
pixel_pitch_w = (sensor_size["width"] / fmts[0]["width"] * 1E3)
print "Assert pixel_pitch WxH: %.2f um, %.2f um" % (pixel_pitch_w,
pixel_pitch_h)
- assert 1.0 <= pixel_pitch_w <= 10
- assert 1.0 <= pixel_pitch_h <= 10
+ assert 0.9 <= pixel_pitch_w <= 10
+ assert 0.9 <= pixel_pitch_h <= 10
assert 0.333 <= pixel_pitch_w/pixel_pitch_h <= 3.0
diag = math.sqrt(sensor_size["height"] ** 2 +
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 81532c8..ecd35f3 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.verifier"
android:versionCode="5"
- android:versionName="7.1_r7">
+ android:versionName="7.1_r8">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25"/>
diff --git a/apps/CtsVerifier/assets/report/compatibility_result.xsl b/apps/CtsVerifier/assets/report/compatibility_result.xsl
index b8c1245..1e9ec31 100644
--- a/apps/CtsVerifier/assets/report/compatibility_result.xsl
+++ b/apps/CtsVerifier/assets/report/compatibility_result.xsl
@@ -82,12 +82,6 @@
</td>
</tr>
<tr>
- <td class="rowtitle">Tests Not Executed</td>
- <td>
- <xsl:value-of select="Result/Summary/@not_executed"/>
- </td>
- </tr>
- <tr>
<td class="rowtitle">Modules Done</td>
<td>
<xsl:value-of select="Result/Summary/@modules_done"/>
@@ -134,7 +128,6 @@
<th>Module</th>
<th>Passed</th>
<th>Failed</th>
- <th>Not Executed</th>
<th>Total Tests</th>
</tr>
<xsl:for-each select="Result/Module">
@@ -150,10 +143,7 @@
<xsl:value-of select="count(TestCase/Test[@result = 'fail'])"/>
</td>
<td>
- <xsl:value-of select="@not_executed"/>
- </td>
- <td>
- <xsl:value-of select="count(TestCase/Test) + @not_executed"/>
+ <xsl:value-of select="count(TestCase/Test)"/>
</td>
</tr>
</xsl:for-each> <!-- end Module -->
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 79f364d..d2dd9ef 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -64,6 +64,8 @@
private ComponentName mAdminReceiverComponent;
private KeyguardManager mKeyguardManager;
+ private ByodFlowTestHelper mByodFlowTestHelper;
+
private DialogTestListItem mProfileOwnerInstalled;
private DialogTestListItem mDiskEncryptionTest;
private DialogTestListItem mProfileAccountVisibleTest;
@@ -114,10 +116,12 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mByodFlowTestHelper = new ByodFlowTestHelper(this);
mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
- enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+ mByodFlowTestHelper.setup();
+
mPrepareTestButton.setText(R.string.provisioning_byod_start);
mPrepareTestButton.setOnClickListener(new OnClickListener() {
@Override
@@ -186,8 +190,7 @@
public void finish() {
// Pass and fail buttons are known to call finish() when clicked, and this is when we want to
// clean up the provisioned profile.
- Utils.requestDeleteManagedProfile(this);
- enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+ mByodFlowTestHelper.tearDown();
super.finish();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
new file mode 100644
index 0000000..fb92248
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
@@ -0,0 +1,50 @@
+package com.android.cts.verifier.managedprovisioning;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+public class ByodFlowTestHelper {
+ private Context mContext;
+ private PackageManager mPackageManager;
+
+ public ByodFlowTestHelper(Context context) {
+ this.mContext = context;
+ this.mPackageManager = mContext.getPackageManager();
+ }
+
+ public void setup() {
+ setComponentsEnabledState(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+ }
+
+ /**
+ * Clean up things. This has to be working even it is called multiple times.
+ */
+ public void tearDown() {
+ Utils.requestDeleteManagedProfile(mContext);
+ setComponentsEnabledState(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+ }
+
+ /**
+ * Disable or enable app components in the current profile. When they are disabled only the
+ * counterpart in the other profile can respond (via cross-profile intent filter).
+ *
+ * @param enabledState {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED} or
+ * {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}
+ */
+ private void setComponentsEnabledState(final int enabledState) {
+ final String[] components = {
+ ByodHelperActivity.class.getName(),
+ WorkStatusTestActivity.class.getName(),
+ PermissionLockdownTestActivity.ACTIVITY_ALIAS,
+ AuthenticationBoundKeyTestActivity.class.getName(),
+ VpnTestActivity.class.getName(),
+ CommandReceiverActivity.class.getName(),
+ SetSupportMessageActivity.class.getName()
+ };
+ for (String component : components) {
+ mPackageManager.setComponentEnabledSetting(new ComponentName(mContext, component),
+ enabledState, PackageManager.DONT_KILL_APP);
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 755d185..2663364 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -75,6 +75,10 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // Tidy up in case previous run crashed.
+ new ByodFlowTestHelper(this).tearDown();
+
if (ACTION_CHECK_DEVICE_OWNER.equals(getIntent().getAction())) {
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
Context.DEVICE_POLICY_SERVICE);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
index 1d23175..7ab8e7e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
@@ -80,6 +80,9 @@
return;
}
+ // Tidy up in case previous run crashed.
+ new ByodFlowTestHelper(this).tearDown();
+
setContentView(R.layout.requesting_bugreport_device_owner);
setInfoResources(R.string.device_owner_requesting_bugreport_tests,
R.string.device_owner_requesting_bugreport_tests_info, 0);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
index e00d353..e74f07e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
@@ -154,6 +154,8 @@
return pm.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS);
case PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_ACCESSIBILITY_SERVICE:
return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
+ case PolicyTransparencyTestActivity.TEST_CHECK_LOCK_SCREEN_INFO:
+ return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
default:
return true;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
index db275f2..2ea5cf9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
@@ -58,9 +58,8 @@
static void requestDeleteManagedProfile(Context context) {
try {
Intent intent = new Intent(ByodHelperActivity.ACTION_REMOVE_MANAGED_PROFILE);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
- String message = context.getString(R.string.provisioning_byod_delete_profile);
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
catch (ActivityNotFoundException e) {
Log.d(TAG, "requestDeleteProfileOwner: ActivityNotFoundException", e);
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index c49340e..52dc79b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -224,6 +224,10 @@
// Verify they can't poke at each other
runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testUserIsolation", owner);
runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testUserIsolation", secondary);
+
+ // Verify they can't access other users' content using media provider
+ runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testMediaProviderUserIsolation", owner);
+ runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testMediaProviderUserIsolation", secondary);
} finally {
getDevice().uninstallPackage(MULTIUSER_PKG);
removeUsersForTest(users);
diff --git a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/src/com/android/cts/multiuserstorageapp/MultiUserStorageTest.java b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/src/com/android/cts/multiuserstorageapp/MultiUserStorageTest.java
index d9f00d2..3cf1443 100644
--- a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/src/com/android/cts/multiuserstorageapp/MultiUserStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/src/com/android/cts/multiuserstorageapp/MultiUserStorageTest.java
@@ -17,15 +17,20 @@
package com.android.cts.multiuserstorageapp;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileNoAccess;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPathsExceptObb;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.writeInt;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.net.Uri;
import android.os.Environment;
import android.test.AndroidTestCase;
import android.util.Log;
import java.io.File;
+import java.io.FileNotFoundException;
/**
* Test multi-user emulated storage environment, ensuring that each user has
@@ -138,6 +143,36 @@
}
}
+ /**
+ * Verify that files cannot be accessed through media provider.
+ */
+ public void testMediaProviderUserIsolation() throws Exception {
+ final File myPath = Environment.getExternalStorageDirectory();
+ final int myId = android.os.Process.myUid() / 100000;
+ assertEquals(String.valueOf(myId), myPath.getName());
+
+ Log.d(TAG, "My path is " + myPath + " user id " + myId);
+ final File basePath = myPath.getParentFile();
+ for (int i = 0; i < 128; i++) {
+ if (i == myId) continue;
+ final File otherPath = new File(basePath,i + "/" + FILE_SINGLETON);
+ assertFileNoAccess(otherPath);
+
+ final String URI_MEDIA_STRING = "content://media/external/audio/media/";
+ ContentResolver contentResolver = mContext.getContentResolver();
+ ContentValues cv = new ContentValues();
+ cv.put("_data", otherPath.getAbsolutePath());
+ contentResolver.insert(Uri.parse(URI_MEDIA_STRING), cv);
+
+ try {
+ mContext.getContentResolver().openInputStream(Uri.parse(URI_MEDIA_STRING));
+ fail("Accessing through media provider should not be allowed. Path " + myPath);
+ } catch (FileNotFoundException expected) {
+ // OK
+ }
+ }
+ }
+
private File buildApiObbPath(String file) {
return new File(getContext().getObbDir(), file);
}
diff --git a/hostsidetests/theme/assets/tvdpi.zip b/hostsidetests/theme/assets/tvdpi.zip
new file mode 100755
index 0000000..553657f
--- /dev/null
+++ b/hostsidetests/theme/assets/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java b/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
index a8871a3..7cea5e5 100644
--- a/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
+++ b/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
@@ -99,8 +99,9 @@
getDevice().executeShellCommand(SLEEP_COMMAND);
// Start the APK and wait for it to complete.
getDevice().executeShellCommand(START_COMMAND);
- // Give the activity some time to start
- Thread.sleep(500);
+ // Adding delay for OEM specific features which could delay the time of printing the
+ // test log. Please refer to b/62075150 for additional details.
+ Thread.sleep(1000);
// Dump logcat.
String logs = getDevice().executeAdbCommand(
"logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
diff --git a/tests/camera/AndroidTest.xml b/tests/camera/AndroidTest.xml
index 0f53255..34b116f 100644
--- a/tests/camera/AndroidTest.xml
+++ b/tests/camera/AndroidTest.xml
@@ -21,7 +21,7 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.camera.cts" />
<option name="runtime-hint" value="12m7s" />
- <!-- test-timeout unit is ms, value = 60 min -->
- <option name="test-timeout" value="3600000" />
+ <!-- test-timeout unit is ms, value = 200 min -->
+ <option name="test-timeout" value="12000000" />
</test>
</configuration>
diff --git a/tests/leanbackjank/app/Android.mk b/tests/leanbackjank/app/Android.mk
index e2cbf89..dbd28f4 100644
--- a/tests/leanbackjank/app/Android.mk
+++ b/tests/leanbackjank/app/Android.mk
@@ -47,4 +47,6 @@
--extra-packages android.support.v17.leanback \
--extra-packages android.support.v7.recyclerview
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/leanbackjank/app/proguard.flags b/tests/leanbackjank/app/proguard.flags
new file mode 100644
index 0000000..0674e77
--- /dev/null
+++ b/tests/leanbackjank/app/proguard.flags
@@ -0,0 +1 @@
+-dontobfuscate
\ No newline at end of file
diff --git a/tests/leanbackjank/app/res/values/dimens.xml b/tests/leanbackjank/app/res/values/dimens.xml
new file mode 100644
index 0000000..30792fd
--- /dev/null
+++ b/tests/leanbackjank/app/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <dimen name="shadow_unfocused_z">0dp</dimen>
+ <dimen name="shadow_focused_z">10dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
index bedbc5d..4e6b55d 100644
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
+++ b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
@@ -17,11 +17,15 @@
package android.leanbackjank.app.presenter;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.support.v17.leanback.widget.ImageCardView;
import android.support.v17.leanback.widget.Presenter;
import android.view.ViewGroup;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.drawable.GlideDrawable;
+import com.bumptech.glide.request.RequestListener;
+import com.bumptech.glide.request.target.Target;
import android.leanbackjank.app.R;
import android.leanbackjank.app.model.Movie;
@@ -46,25 +50,17 @@
ImageCardView cardView = new ImageCardView(parent.getContext()) {
@Override
public void setSelected(boolean selected) {
- updateCardBackgroundColor(this, selected);
+ findViewById(R.id.info_field).setBackgroundColor(
+ selected ? sSelectedBackgroundColor : sDefaultBackgroundColor);
super.setSelected(selected);
}
};
cardView.setFocusable(true);
cardView.setFocusableInTouchMode(true);
- updateCardBackgroundColor(cardView, false);
return new ViewHolder(cardView);
}
- private static void updateCardBackgroundColor(ImageCardView view, boolean selected) {
- int color = selected ? sSelectedBackgroundColor : sDefaultBackgroundColor;
- // Both background colors should be set because the view's background is temporarily visible
- // during animations.
- view.setBackgroundColor(color);
- view.findViewById(R.id.info_field).setBackgroundColor(color);
- }
-
@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
Movie movie = (Movie) item;
@@ -73,10 +69,27 @@
cardView.setTitleText(movie.getTitle());
cardView.setContentText(movie.getStudio());
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT);
+ cardView.setBackgroundColor(sDefaultBackgroundColor);
+
Glide.with(viewHolder.view.getContext())
.load(R.drawable.gradation)
.centerCrop()
.error(mDefaultCardImage)
+ .listener(new RequestListener<Integer, GlideDrawable>() {
+ @Override
+ public boolean onException(Exception e, Integer i, Target<GlideDrawable> target,
+ boolean b) {
+ return false;
+ }
+
+ @Override
+ public boolean onResourceReady(GlideDrawable glideDrawable, Integer i,
+ Target<GlideDrawable> target, boolean b, boolean b1) {
+ // Remove the background color to reduce overdraw.
+ cardView.setBackground(null);
+ return false;
+ }
+ })
.into(cardView.getMainImageView());
}
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java b/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
index 399681c..ee04924 100644
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
+++ b/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
@@ -15,6 +15,7 @@
package android.leanbackjank.app.ui;
import android.content.Intent;
+import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.leanbackjank.app.IntentKeys;
import android.leanbackjank.app.R;
@@ -32,6 +33,7 @@
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v17.leanback.widget.ShadowOverlayHelper;
import android.util.DisplayMetrics;
import android.view.View;
@@ -166,7 +168,17 @@
}
public void buildRowAdapterItems(HashMap<String, List<Movie>> data) {
- mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+ ListRowPresenter listRowPresenter = new ListRowPresenter() {
+ @Override
+ protected ShadowOverlayHelper.Options createShadowOverlayOptions() {
+ Resources res = getResources();
+ ShadowOverlayHelper.Options options = new ShadowOverlayHelper.Options();
+ options.dynamicShadowZ(res.getDimension(R.dimen.shadow_unfocused_z),
+ res.getDimension(R.dimen.shadow_focused_z));
+ return options;
+ }
+ };
+ mRowsAdapter = new ArrayObjectAdapter(listRowPresenter);
CardPresenter cardPresenter = new CardPresenter();
int i = 0;
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
index 33b99a4..1183c11 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
@@ -48,6 +48,7 @@
private AnimationDrawable mAnimationDrawable;
private Resources mResources;
+ private boolean mInitialOneShotValue;
public AnimationDrawableTest() {
super("android.graphics.cts", ImageViewCtsActivity.class);
@@ -64,6 +65,7 @@
ImageView imageView = (ImageView) activity.findViewById(R.id.imageview);
imageView.setBackgroundResource(R.drawable.animationdrawable);
mAnimationDrawable = (AnimationDrawable) imageView.getBackground();
+ mInitialOneShotValue = mAnimationDrawable.isOneShot();
}
});
} catch (Throwable t) {
@@ -71,6 +73,12 @@
}
}
+ @Override
+ protected void tearDown() throws Exception {
+ mAnimationDrawable.setOneShot(mInitialOneShotValue);
+ super.tearDown();
+ }
+
public void testConstructor() {
mAnimationDrawable = new AnimationDrawable();
// Check the values set in the constructor
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index eda78e8..66dcef6 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -46,7 +46,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil ctsdeviceutil compatibility-device-util ctstestserver ctstestrunner ndkaudio
-LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni libaudio_jni libnativehelper_compat_libc++ libndkaudioLib
+LOCAL_JNI_SHARED_LIBRARIES := libctsmediadrm_jni libctsmediacodec_jni libaudio_jni libnativehelper_compat_libc++ libndkaudioLib
# do not compress VP9 video files
LOCAL_AAPT_FLAGS := -0 .vp9
diff --git a/tests/tests/media/libmediandkjni/AMediaObjects.h b/tests/tests/media/libmediandkjni/AMediaObjects.h
new file mode 100644
index 0000000..c4d5397
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/AMediaObjects.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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 AMEDIAOBJECTS_H_
+#define AMEDIAOBJECTS_H_
+
+#include <utils/Log.h>
+
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+
+namespace {
+
+// Simple class to manage deletion of AMedia objects
+class AMediaObjects {
+ public:
+ AMediaObjects();
+ virtual ~AMediaObjects();
+
+ void setCrypto(AMediaCrypto* const theCrypto) {
+ mCrypto = theCrypto;
+ }
+ void setDrm(AMediaDrm* const theDrm) {
+ mDrm = theDrm;
+ }
+ void setVideoExtractor(AMediaExtractor* const theExtractor) {
+ mVideoExtractor = theExtractor;
+ }
+ void setAudioExtractor(AMediaExtractor* const theExtractor) {
+ mAudioExtractor = theExtractor;
+ }
+
+ AMediaCrypto* getCrypto() const { return mCrypto; }
+ AMediaDrm* getDrm() const { return mDrm; }
+ AMediaExtractor* getAudioExtractor() const { return mAudioExtractor; }
+ AMediaExtractor* getVideoExtractor() const { return mVideoExtractor; }
+
+ private:
+ AMediaCrypto *mCrypto;
+ AMediaDrm* mDrm;
+ AMediaExtractor* mAudioExtractor;
+ AMediaExtractor* mVideoExtractor;
+
+ // Disallow copy and assignment
+ AMediaObjects(const AMediaObjects&);
+ void operator=(const AMediaObjects&);
+};
+
+AMediaObjects::AMediaObjects(void) : mCrypto(NULL), mDrm(NULL),
+ mAudioExtractor(NULL), mVideoExtractor(NULL) {
+}
+
+AMediaObjects::~AMediaObjects() {
+ if (mCrypto) {
+ AMediaCrypto_delete(mCrypto);
+ }
+ if (mAudioExtractor) {
+ AMediaExtractor_delete(mAudioExtractor);
+ }
+ if (mVideoExtractor) {
+ AMediaExtractor_delete(mVideoExtractor);
+ }
+ if (mDrm) {
+ AMediaDrm_release(mDrm);
+ }
+}
+
+} // anonymous namespace
+#endif // AMEDIAOBJECTS_H_
+
diff --git a/tests/tests/media/libmediandkjni/Android.mk b/tests/tests/media/libmediandkjni/Android.mk
index 259c95e..5aa222a 100644
--- a/tests/tests/media/libmediandkjni/Android.mk
+++ b/tests/tests/media/libmediandkjni/Android.mk
@@ -14,27 +14,66 @@
#
LOCAL_PATH := $(call my-dir)
+#------------------------------------------------------------------------------
+# Builds libctsmediacodec_jni.so
+#
include $(CLEAR_VARS)
-LOCAL_MODULE := libctsmediacodec_jni
+LOCAL_MODULE := libctsmediacodec_jni
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
- native-media-jni.cpp \
- codec-utils-jni.cpp \
- md5_utils.cpp
+ native-media-jni.cpp \
+ codec-utils-jni.cpp \
+ md5_utils.cpp
LOCAL_C_INCLUDES := \
- $(JNI_H_INCLUDE) \
- system/core/include
+ $(JNI_H_INCLUDE) \
+ system/core/include
LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
-LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog libmediandk
+LOCAL_SHARED_LIBRARIES := \
+ libandroid libnativehelper_compat_libc++ \
+ liblog libmediandk libEGL
-LOCAL_SDK_VERSION := 23
+LOCAL_SDK_VERSION := 24
-LOCAL_CFLAGS := -Werror -Wall
+LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES -std=gnu++14
+
+include $(BUILD_SHARED_LIBRARY)
+
+#------------------------------------------------------------------------------
+# Builds libctsmediadrm_jni.so
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctsmediadrm_jni
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ CtsMediaDrmJniOnLoad.cpp \
+ codec-utils-jni.cpp \
+ md5_utils.cpp \
+ native-mediadrm-jni.cpp \
+
+LOCAL_C_INCLUDES := \
+ $(JNI_H_INCLUDE) \
+ system/core/include
+
+LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid libnativehelper_compat_libc++ \
+ liblog libmediandk libdl libEGL
+
+LOCAL_SDK_VERSION := 24
+
+LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES
+
+LOCAL_NDK_STL_VARIANT := c++_static
include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
new file mode 100644
index 0000000..24714a3
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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 <stdio.h>
+
+extern int register_android_media_cts_NativeClearKeySystemTest(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM *vm, void */*reserved*/) {
+ JNIEnv *env = NULL;
+
+ if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+ return JNI_ERR;
+ }
+
+ if (register_android_media_cts_NativeClearKeySystemTest(env)) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/media/libmediandkjni/md5_utils.cpp b/tests/tests/media/libmediandkjni/md5_utils.cpp
index f4f893a..7850cac 100644
--- a/tests/tests/media/libmediandkjni/md5_utils.cpp
+++ b/tests/tests/media/libmediandkjni/md5_utils.cpp
@@ -157,7 +157,7 @@
*/
void
MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) {
- register UWORD32 a, b, c, d;
+ /*register*/ UWORD32 a, b, c, d;
a = buf[0];
b = buf[1];
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
new file mode 100644
index 0000000..571cec4
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define TAG "NativeMediaDrm"
+
+#include <utils/Log.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <assert.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <android/native_window_jni.h>
+
+#include "AMediaObjects.h"
+
+#include "media/NdkMediaCodec.h"
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaFormat.h"
+#include "media/NdkMediaMuxer.h"
+
+typedef std::vector<uint8_t> Uuid;
+
+struct fields_t {
+ jfieldID surface;
+ jfieldID mimeType;
+ jfieldID audioUrl;
+ jfieldID videoUrl;
+};
+
+struct PlaybackParams {
+ jobject surface;
+ jstring mimeType;
+ jstring audioUrl;
+ jstring videoUrl;
+};
+
+static fields_t gFieldIds;
+static bool gGotVendorDefinedEvent = false;
+
+static const size_t kPlayTimeSeconds = 30;
+static const size_t kUuidSize = 16;
+
+static const uint8_t kClearKeyUuid[kUuidSize] = {
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
+};
+
+// The test content is not packaged with clearkey UUID,
+// we have to use a canned clearkey pssh for the test.
+static const uint8_t kClearkeyPssh[] = {
+ // BMFF box header (4 bytes size + 'pssh')
+ 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+ // full box header (version = 1 flags = 0)
+ 0x01, 0x00, 0x00, 0x00,
+ // system id
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ // number of key ids
+ 0x00, 0x00, 0x00, 0x01,
+ // key id
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ // size of data, must be zero
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t kKeyRequestData[] = {
+ 0x7b, 0x22, 0x6b, 0x69, 0x64,
+ 0x73, 0x22, 0x3a, 0x5b, 0x22,
+ 0x4d, 0x44, 0x41, 0x77, 0x4d,
+ 0x44, 0x41, 0x77, 0x4d, 0x44,
+ 0x41, 0x77, 0x4d, 0x44, 0x41,
+ 0x77, 0x4d, 0x44, 0x41, 0x77,
+ 0x4d, 0x41, 0x22, 0x5d, 0x2c,
+ 0x22, 0x74, 0x79, 0x70, 0x65,
+ 0x22, 0x3a, 0x22, 0x74, 0x65,
+ 0x6d, 0x70, 0x6f, 0x72, 0x61,
+ 0x72, 0x79, 0x22, 0x7d
+};
+
+static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
+
+// base 64 encoded JSON response string, must not contain padding character '='
+static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
+ "\"kid\":\"MDAwMDAwMDAwMDAwMDAwMA\",\"k\":" \
+ "\"Pwoz80CYueIrwHjgobXoVA\"}]}";
+
+static bool isUuidSizeValid(Uuid uuid) {
+ return (uuid.size() == kUuidSize);
+}
+
+static std::vector<uint8_t> jbyteArrayToVector(
+ JNIEnv* env, jbyteArray const &byteArray) {
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(
+ env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
+ std::vector<uint8_t> vector;
+ for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
+ vector.push_back(buffer[i]);
+ }
+ return vector;
+}
+
+static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
+ Uuid juuid;
+ juuid.resize(0);
+ if (uuid != NULL) {
+ juuid = jbyteArrayToVector(env, uuid);
+ }
+ return juuid;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
+
+ if (NULL == uuid) {
+ jniThrowException(env, "java/lang/NullPointerException", "null uuid");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (isUuidSizeValid(juuid)) {
+ return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
+ } else {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ }
+ return JNI_FALSE;
+}
+
+void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams ¶ms) {
+ params.surface = env->GetObjectField(
+ playbackParams, gFieldIds.surface);
+
+ params.mimeType = static_cast<jstring>(env->GetObjectField(
+ playbackParams, gFieldIds.mimeType));
+
+ params.audioUrl = static_cast<jstring>(env->GetObjectField(
+ playbackParams, gFieldIds.audioUrl));
+
+ params.videoUrl = static_cast<jstring>(env->GetObjectField(
+ playbackParams, gFieldIds.videoUrl));
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative(
+ JNIEnv* env, jclass clazz, jbyteArray uuid,
+ jstring name, jobject outValue) {
+
+ if (NULL == uuid || NULL == name || NULL == outValue) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "One or more null input parameters");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (!isUuidSizeValid(juuid)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ return JNI_FALSE;
+ }
+
+ AMediaObjects aMediaObjects;
+ aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+ if (NULL == aMediaObjects.getDrm()) {
+ jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+ return JNI_FALSE;
+ }
+
+ const char *utf8_name = env->GetStringUTFChars(name, NULL);
+ const char *utf8_outValue = NULL;
+ media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+ utf8_name, &utf8_outValue);
+ env->ReleaseStringUTFChars(name, utf8_name);
+
+ if (NULL != utf8_outValue) {
+ clazz = env->GetObjectClass(outValue);
+ jmethodID mId = env->GetMethodID (clazz, "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+ jstring outString = env->NewStringUTF(
+ static_cast<const char *>(utf8_outValue));
+ env->CallObjectMethod(outValue, mId, outString);
+ } else {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "get property string returns %d", status);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest__testPsshNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
+
+ if (NULL == uuid || NULL == videoUrl) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "null uuid or null videoUrl");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (!isUuidSizeValid(juuid)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ return JNI_FALSE;
+ }
+
+ AMediaObjects aMediaObjects;
+ aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+ const char* url = env->GetStringUTFChars(videoUrl, 0);
+ if (url) {
+ media_status_t status = AMediaExtractor_setDataSource(
+ aMediaObjects.getVideoExtractor(), url);
+ env->ReleaseStringUTFChars(videoUrl, url);
+
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "set video data source error=%d", status);
+ return JNI_FALSE;
+ }
+ }
+
+ PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
+ if (psshInfo == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
+ return JNI_FALSE;
+ }
+
+ jboolean testResult = JNI_FALSE;
+ for (size_t i = 0; i < psshInfo->numentries; i++) {
+ PsshEntry *entry = &psshInfo->entries[i];
+
+ if (0 == memcmp(entry->uuid, kClearKeyUuid, sizeof(entry->uuid))) {
+ aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+ if (aMediaObjects.getDrm()) {
+ testResult = JNI_TRUE;
+ } else {
+ ALOGE("Failed to create media drm=%zd", i);
+ testResult = JNI_FALSE;
+ }
+ break;
+ }
+ }
+ return testResult;
+}
+
+static bool isVideo(const char* mime) {
+ return !strncmp(mime, "video/", 6) ? true : false;
+}
+
+static bool isAudio(const char* mime) {
+ return !strncmp(mime, "audio/", 6) ? true : false;
+}
+
+static void addTrack(const AMediaFormat* format,
+ const char* mime, const AMediaCrypto* crypto,
+ const ANativeWindow* window, AMediaCodec** codec) {
+
+ *codec = AMediaCodec_createDecoderByType(mime);
+ if (codec == NULL) {
+ ALOGE("cannot create codec for %s", mime);
+ return;
+ }
+
+ AMediaCodec_configure(*codec, format,
+ const_cast<ANativeWindow*>(window),
+ const_cast<AMediaCrypto*>(crypto), 0);
+}
+
+static void addTracks(const AMediaExtractor* extractor,
+ const AMediaCrypto* crypto, const ANativeWindow* window,
+ AMediaCodec** codec) {
+ size_t numTracks = AMediaExtractor_getTrackCount(
+ const_cast<AMediaExtractor*>(extractor));
+
+ AMediaFormat* trackFormat = NULL;
+ for (size_t i = 0; i < numTracks; ++i) {
+ trackFormat = AMediaExtractor_getTrackFormat(
+ const_cast<AMediaExtractor*>(extractor), i);
+ if (trackFormat) {
+ ALOGV("track %zd format: %s", i,
+ AMediaFormat_toString(trackFormat));
+
+ const char* mime = "";
+ if (!AMediaFormat_getString(
+ trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+ ALOGE("no mime type");
+
+ AMediaFormat_delete(trackFormat);
+ return;
+ } else if (isAudio(mime) || isVideo(mime)) {
+ AMediaExtractor_selectTrack(
+ const_cast<AMediaExtractor*>(extractor), i);
+ ALOGV("track %zd codec format: %s", i,
+ AMediaFormat_toString(trackFormat));
+
+ addTrack(trackFormat, mime, crypto, window, codec);
+ AMediaCodec_start(*codec);
+ AMediaCodec_flush(*codec);
+ AMediaExtractor_seekTo(
+ const_cast<AMediaExtractor*>(extractor), 0,
+ AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+ }
+ AMediaFormat_delete(trackFormat);
+ }
+ }
+}
+
+static int64_t getSystemNanoTime() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
+
+static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
+ int64_t* presentationTimeUs, bool* eosReached) {
+ media_status_t status = AMEDIA_OK;
+
+ ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
+ if (bufferIndex >= 0) {
+ size_t bufsize;
+ uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
+
+ int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
+ if (sampleSize < 0) {
+ sampleSize = 0;
+ *eosReached = true;
+ }
+
+ *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
+
+ AMediaCodecCryptoInfo *cryptoInfo =
+ AMediaExtractor_getSampleCryptoInfo(extractor);
+
+ if (cryptoInfo) {
+ status = AMediaCodec_queueSecureInputBuffer(
+ codec, bufferIndex, 0, cryptoInfo,
+ *presentationTimeUs,
+ *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ AMediaCodecCryptoInfo_delete(cryptoInfo);
+ } else {
+ status = AMediaCodec_queueInputBuffer(
+ codec, bufferIndex, 0, sampleSize,
+ *presentationTimeUs,
+ *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ }
+ AMediaExtractor_advance(extractor);
+ }
+}
+
+static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
+ int64_t* startTimeNano) {
+
+ AMediaCodecBufferInfo info;
+ ssize_t bufferIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
+ if (bufferIndex >= 0) {
+ if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+ return true; // eos reached
+ }
+
+ if (*startTimeNano < 0) {
+ *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
+ }
+ int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
+ getSystemNanoTime();
+ if (delay > 0) {
+ usleep(delay / 1000);
+ }
+
+ AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
+ } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ ALOGV("output buffers changed");
+ } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
+ ALOGV("format changed to: %s", AMediaFormat_toString(format));
+ AMediaFormat_delete(format);
+ } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ ALOGV("no output buffer right now");
+ usleep(20000);
+ } else {
+ ALOGV("unexpected info code: %zd", bufferIndex);
+ }
+ return false;
+}
+
+static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
+ PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
+
+ ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
+ AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
+ AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
+
+ AMediaCodec* audioCodec = NULL;
+ AMediaCodec* videoCodec = NULL;
+ AMediaCrypto* crypto = NULL;
+
+ crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
+ if (crypto == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "failed to create crypto object");
+ return JNI_FALSE;
+ }
+
+ addTracks(audioExtractor, NULL, NULL, &audioCodec);
+
+ addTracks(videoExtractor, crypto, window, &videoCodec);
+
+ bool sawAudioInputEos = false;
+ bool sawAudioOutputEos = false;
+ bool sawVideoInputEos = false;
+ bool sawVideoOutputEos = false;
+ int64_t videoPresentationTimeUs = 0;
+ int64_t videoStartTimeNano = -1;
+ struct timespec timeSpec;
+ clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+ time_t startTimeSec = timeSpec.tv_sec;
+
+ while (!sawAudioOutputEos && !sawVideoOutputEos) {
+ if (!sawVideoInputEos) {
+ fillDecoder(videoCodec, videoExtractor,
+ &videoPresentationTimeUs, &sawVideoInputEos);
+ }
+
+ if (!sawAudioInputEos) {
+ // skip audio, still need to advance the audio extractor
+ AMediaExtractor_advance(audioExtractor);
+ }
+
+ if (!sawVideoOutputEos) {
+ sawVideoOutputEos = drainDecoder(videoCodec, videoPresentationTimeUs,
+ &videoStartTimeNano);
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+ if (timeSpec.tv_sec >= static_cast<time_t>(
+ (startTimeSec + kPlayTimeSeconds))) {
+ // stop reading samples and drain the output buffers
+ sawAudioInputEos = sawVideoInputEos = true;
+ sawAudioOutputEos = true; // ignore audio
+ }
+ }
+
+ if (audioCodec) {
+ AMediaCodec_stop(audioCodec);
+ AMediaCodec_delete(audioCodec);
+ }
+ if (videoCodec) {
+ AMediaCodec_stop(videoCodec);
+ AMediaCodec_delete(videoCodec);
+ }
+
+ AMediaCrypto_delete(crypto);
+ ANativeWindow_release(window);
+ return JNI_TRUE;
+}
+
+static void listener(
+ AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
+ AMediaDrmEventType eventType,
+ int /*extra*/, const uint8_t* /*data*/, size_t /*dataSize*/) {
+
+ switch (eventType) {
+ case EVENT_PROVISION_REQUIRED:
+ ALOGD("EVENT_PROVISION_REQUIRED received");
+ break;
+ case EVENT_KEY_REQUIRED:
+ ALOGD("EVENT_KEY_REQUIRED received");
+ break;
+ case EVENT_KEY_EXPIRED:
+ ALOGD("EVENT_KEY_EXPIRED received");
+ break;
+ case EVENT_VENDOR_DEFINED:
+ gGotVendorDefinedEvent = true;
+ ALOGD("EVENT_VENDOR_DEFINED received");
+ break;
+ default:
+ ALOGD("Unknown event received");
+ break;
+ }
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
+ if (NULL == uuid || NULL == playbackParams) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "null uuid or null playback parameters");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (!isUuidSizeValid(juuid)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ return JNI_FALSE;
+ }
+
+ PlaybackParams params;
+ initPlaybackParams(env, playbackParams, params);
+
+ AMediaObjects aMediaObjects;
+ media_status_t status = AMEDIA_OK;
+ aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+ if (NULL == aMediaObjects.getDrm()) {
+ jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+ return JNI_FALSE;
+ }
+
+ status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
+ if (status != AMEDIA_OK) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "setOnEventListener failed");
+ return JNI_FALSE;
+ }
+
+ aMediaObjects.setAudioExtractor(AMediaExtractor_new());
+ const char* url = env->GetStringUTFChars(params.audioUrl, 0);
+ if (url) {
+ status = AMediaExtractor_setDataSource(
+ aMediaObjects.getAudioExtractor(), url);
+ env->ReleaseStringUTFChars(params.audioUrl, url);
+
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "set audio data source error=%d", status);
+ return JNI_FALSE;
+ }
+ }
+
+ aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+ url = env->GetStringUTFChars(params.videoUrl, 0);
+ if (url) {
+ status = AMediaExtractor_setDataSource(
+ aMediaObjects.getVideoExtractor(), url);
+ env->ReleaseStringUTFChars(params.videoUrl, url);
+
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "set video data source error=%d", status);
+ return JNI_FALSE;
+ }
+ }
+
+ AMediaDrmSessionId sessionId;
+ status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
+ if (status != AMEDIA_OK) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "openSession failed");
+ return JNI_FALSE;
+ }
+
+ // Pointer to keyRequest memory, which remains until the next
+ // AMediaDrm_getKeyRequest call or until the drm object is released.
+ const uint8_t* keyRequest;
+ size_t keyRequestSize = 0;
+
+ // The server recognizes "video/mp4" but not "video/avc".
+ status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(), &sessionId,
+ kClearkeyPssh, sizeof(kClearkeyPssh),
+ "video/mp4" /*mimeType*/, KEY_TYPE_STREAMING,
+ NULL, 0, &keyRequest, &keyRequestSize);
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "getKeyRequest failed, error = %d", status);
+ AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ return JNI_FALSE;
+ }
+
+ if (kKeyRequestSize != keyRequestSize) {
+ ALOGE("Invalid keyRequestSize %zd", keyRequestSize);
+ return JNI_FALSE;
+ }
+
+ if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
+ ALOGE("Invalid keyRequest data is returned");
+ return JNI_FALSE;
+ }
+
+ AMediaDrmKeySetId keySetId;
+ gGotVendorDefinedEvent = false;
+ status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
+ reinterpret_cast<const uint8_t*>(kResponse),
+ sizeof(kResponse), &keySetId);
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "provideKeyResponse failed, error = %d", status);
+ AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ return JNI_FALSE;
+ }
+
+ // Check if the event listener has received the expected event sent by
+ // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
+ const char *utf8_outValue = NULL;
+ status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+ "listenerTestSupport", &utf8_outValue);
+ if (status == AMEDIA_OK && NULL != utf8_outValue) {
+ std::string eventType(utf8_outValue);
+ if (eventType.compare("true") == 0) {
+ int count = 0;
+ while (!gGotVendorDefinedEvent && count++ < 5) {
+ // Prevents race condition when the event arrives late
+ usleep(2000);
+ }
+ if (!gGotVendorDefinedEvent) {
+ ALOGE("Event listener did not receive the expected event.");
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "Event listener did not receive the expected event.");
+ AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ return JNI_FALSE;
+ }
+ }
+ }
+
+ playContent(env, aMediaObjects, params, sessionId, juuid);
+
+ status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ if (status != AMEDIA_OK) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "closeSession failed");
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "isCryptoSchemeSupportedNative", "([B)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative },
+
+ { "testClearKeyPlaybackNative",
+ "([BLandroid/media/cts/NativeClearKeySystemTest$PlaybackParams;)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative },
+
+ { "testGetPropertyStringNative",
+ "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative },
+
+ { "testPsshNative", "([BLjava/lang/String;)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest__testPsshNative },
+};
+
+int register_android_media_cts_NativeClearKeySystemTest(JNIEnv* env) {
+ jint result = JNI_ERR;
+ jclass testClass =
+ env->FindClass("android/media/cts/NativeClearKeySystemTest");
+ if (testClass) {
+ jclass playbackParamsClass = env->FindClass(
+ "android/media/cts/NativeClearKeySystemTest$PlaybackParams");
+ if (playbackParamsClass) {
+ jclass surfaceClass =
+ env->FindClass("android/view/Surface");
+ if (surfaceClass) {
+ gFieldIds.surface = env->GetFieldID(playbackParamsClass,
+ "surface", "Landroid/view/Surface;");
+ } else {
+ gFieldIds.surface = NULL;
+ }
+ gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
+ "mimeType", "Ljava/lang/String;");
+ gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
+ "audioUrl", "Ljava/lang/String;");
+ gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
+ "videoUrl", "Ljava/lang/String;");
+ } else {
+ ALOGE("PlaybackParams class not found");
+ }
+
+ } else {
+ ALOGE("NativeClearKeySystemTest class not found");
+ }
+
+ result = env->RegisterNatives(testClass, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+ return result;
+}
diff --git a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
new file mode 100644
index 0000000..9ead8fb
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 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 static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import android.net.Uri;
+import android.os.Build;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import android.cts.util.MediaUtils;
+import com.google.android.collect.Lists;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.UUID;
+
+/**
+ * Tests MediaDrm NDK APIs. ClearKey system uses a subset of NDK APIs,
+ * this test only tests the APIs that are supported by ClearKey system.
+ */
+public class NativeClearKeySystemTest extends MediaPlayerTestBase {
+ private static final String TAG = NativeClearKeySystemTest.class.getSimpleName();
+
+ private static final int CONNECTION_RETRIES = 10;
+ private static final int VIDEO_WIDTH_CENC = 1280;
+ private static final int VIDEO_HEIGHT_CENC = 720;
+ private static final String ISO_BMFF_VIDEO_MIME_TYPE = "video/avc";
+ private static final String ISO_BMFF_AUDIO_MIME_TYPE = "audio/avc";
+ private static final Uri CENC_AUDIO_URL = Uri.parse(
+ "https://storage.googleapis.com/wvmedia/clear/h264/llama/" +
+ "llama_aac_audio.mp4");
+
+ private static final Uri CENC_CLEARKEY_VIDEO_URL = Uri.parse(
+ "https://storage.googleapis.com/wvmedia/clearkey/" +
+ "llama_h264_main_720p_8000.mp4");
+
+ private static final int UUID_BYTE_SIZE = 16;
+ private static final UUID CLEARKEY_SCHEME_UUID =
+ new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+ private static final UUID BAD_SCHEME_UUID =
+ new UUID(0xffffffffffffffffL, 0xffffffffffffffffL);
+ private MediaCodecClearKeyPlayer mMediaCodecPlayer;
+
+ static {
+ try {
+ System.loadLibrary("ctsmediadrm_jni");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+ e.printStackTrace();
+ }
+ try {
+ System.loadLibrary("mediandk");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+ e.printStackTrace();
+ }
+ }
+
+ public static class PlaybackParams {
+ public Surface surface;
+ public String mimeType;
+ public String audioUrl;
+ public String videoUrl;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (false == deviceHasMediaDrm()) {
+ tearDown();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private boolean deviceHasMediaDrm() {
+ // ClearKey is introduced after KitKat.
+ if (Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.KITKAT) {
+ Log.i(TAG, "This test is designed to work after Android KitKat.");
+ return false;
+ }
+ return true;
+ }
+
+ private static final byte[] uuidByteArray(UUID uuid) {
+ ByteBuffer buffer = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
+ buffer.putLong(uuid.getMostSignificantBits());
+ buffer.putLong(uuid.getLeastSignificantBits());
+ return buffer.array();
+ }
+
+ public void testIsCryptoSchemeSupported() throws Exception {
+ assertTrue(isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID)));
+ }
+
+ public void testIsCryptoSchemeNotSupported() throws Exception {
+ assertFalse(isCryptoSchemeSupportedNative(uuidByteArray(BAD_SCHEME_UUID)));
+ }
+
+ public void testPssh() throws Exception {
+ assertTrue(testPsshNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+ CENC_CLEARKEY_VIDEO_URL.toString()));
+ }
+
+ public void testGetPropertyString() throws Exception {
+ StringBuffer value = new StringBuffer();
+ testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value);
+ assertEquals("ClearKey CDM", value.toString());
+ }
+
+ public void testUnknownPropertyString() throws Exception {
+ try {
+ StringBuffer value = new StringBuffer();
+ testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+ "unknown-property", value);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'");
+ assertThat(e.getMessage(), containsString("get property string returns"));
+ }
+ }
+
+ /**
+ * Tests native clear key system playback.
+ */
+ private void testClearKeyPlayback(
+ String mimeType, /*String initDataType,*/ Uri audioUrl, Uri videoUrl,
+ int videoWidth, int videoHeight) throws Exception {
+
+ if (!isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID))) {
+ throw new Error("Crypto scheme is not supported.");
+ }
+
+ IConnectionStatus connectionStatus = new ConnectionStatus(mContext);
+ if (!connectionStatus.isAvailable()) {
+ throw new Error("Network is not available, reason: " +
+ connectionStatus.getNotConnectedReason());
+ }
+
+ // If device is not online, recheck the status a few times.
+ int retries = 0;
+ while (!connectionStatus.isConnected()) {
+ if (retries++ >= CONNECTION_RETRIES) {
+ throw new Error("Device is not online, reason: " +
+ connectionStatus.getNotConnectedReason());
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ connectionStatus.testConnection(videoUrl);
+
+ if (!MediaUtils.checkCodecsForPath(mContext, videoUrl.toString())) {
+ Log.i(TAG, "Device does not support " +
+ videoWidth + "x" + videoHeight + " resolution for " + mimeType);
+ return; // skip
+ }
+
+ PlaybackParams params = new PlaybackParams();
+ params.surface = mActivity.getSurfaceHolder().getSurface();
+ params.mimeType = mimeType;
+ params.audioUrl = audioUrl.toString();
+ params.videoUrl = videoUrl.toString();
+
+ if (!testClearKeyPlaybackNative(
+ uuidByteArray(CLEARKEY_SCHEME_UUID), params)) {
+ Log.e(TAG, "Fails play back using native media drm APIs.");
+ }
+ params.surface.release();
+ }
+
+ private ArrayList<Integer> intVersion(String version) {
+ String versions[] = version.split("\\.");
+
+ ArrayList<Integer> versionNumbers = Lists.newArrayList();
+ for (String subVersion : versions) {
+ versionNumbers.add(Integer.parseInt(subVersion));
+ }
+ return versionNumbers;
+ }
+
+ private static native boolean isCryptoSchemeSupportedNative(final byte[] uuid);
+
+ private static native boolean testClearKeyPlaybackNative(final byte[] uuid,
+ PlaybackParams params);
+
+ private static native boolean testGetPropertyStringNative(final byte[] uuid,
+ final String name, StringBuffer value);
+
+ private static native boolean testPsshNative(final byte[] uuid, final String videoUrl);
+
+ public void testClearKeyPlaybackCenc() throws Exception {
+ testClearKeyPlayback(
+ ISO_BMFF_VIDEO_MIME_TYPE,
+ CENC_AUDIO_URL,
+ CENC_CLEARKEY_VIDEO_URL,
+ VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC);
+ }
+}
diff --git a/tests/tests/net/native/Android.mk b/tests/tests/net/native/Android.mk
new file mode 100644
index 0000000..8338432
--- /dev/null
+++ b/tests/tests/net/native/Android.mk
@@ -0,0 +1,2 @@
+include $(call all-subdir-makefiles)
+
diff --git a/tests/tests/net/native/qtaguid/Android.mk b/tests/tests/net/native/qtaguid/Android.mk
new file mode 100644
index 0000000..4f5bf9f
--- /dev/null
+++ b/tests/tests/net/native/qtaguid/Android.mk
@@ -0,0 +1,71 @@
+# Copyright (C) 2017 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.
+
+# Build the unit tests.
+
+LOCAL_PATH:= $(call my-dir)
+
+test_executable := CtsNativeNetTestCases
+list_executable := $(test_executable)_list
+
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+
+LOCAL_MODULE := $(test_executable)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SRC_FILES := \
+ src/NativeQtaguidTest.cpp
+
+LOCAL_C_INCLUDES := \
+ external/gtest/include \
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ liblog \
+ libcutils \
+
+LOCAL_STATIC_LIBRARIES := \
+ libgtest
+
+LOCAL_CTS_TEST_PACKAGE := android.net.native
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_CFLAGS := -Werror -Wall
+
+include $(BUILD_CTS_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := $(list_executable)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ src/NativeQtaguidTest.cpp
+
+LOCAL_CFLAGS := \
+ -DBUILD_ONLY \
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/tests/tests/net/native/qtaguid/AndroidTest.xml b/tests/tests/net/native/qtaguid/AndroidTest.xml
new file mode 100644
index 0000000..6f4e8f5
--- /dev/null
+++ b/tests/tests/net/native/qtaguid/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 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.
+-->
+<configuration description="Config for CTS Native Network xt_qtaguid test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsNativeNetTestCases->/data/local/tmp/CtsNativeNetTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsNativeNetTestCases" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/tests/tests/net/native/qtaguid/src/NativeQtaguidTest.cpp b/tests/tests/net/native/qtaguid/src/NativeQtaguidTest.cpp
new file mode 100644
index 0000000..0301c81
--- /dev/null
+++ b/tests/tests/net/native/qtaguid/src/NativeQtaguidTest.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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 <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <gtest/gtest.h>
+
+#if !defined(BUILD_ONLY)
+#include <cutils/qtaguid.h>
+#endif
+
+#if !defined(BUILD_ONLY)
+int getCtrlSkInfo(int tag, uid_t uid, uint64_t* sk_addr, int* ref_cnt) {
+ FILE *fp;
+ fp = fopen("/proc/net/xt_qtaguid/ctrl", "r");
+ if (!fp)
+ return -ENOENT;
+ uint64_t full_tag = (uint64_t)tag << 32 | uid;
+ char pattern[40];
+ snprintf(pattern, sizeof(pattern), " tag=0x%" PRIx64 " (uid=%" PRIu32 ")", full_tag, uid);
+
+ size_t len;
+ char *line_buffer = NULL;
+ while(getline(&line_buffer, &len, fp) != -1) {
+ if (strstr(line_buffer, pattern) == NULL)
+ continue;
+ int res;
+ pid_t dummy_pid;
+ uint64_t k_tag;
+ uint32_t k_uid;
+ const int TOTAL_PARAM = 5;
+ res = sscanf(line_buffer, "sock=%" PRIx64 " tag=0x%" PRIx64 " (uid=%" PRIu32 ") "
+ "pid=%u f_count=%u", sk_addr, &k_tag, &k_uid,
+ &dummy_pid, ref_cnt);
+ if (!(res == TOTAL_PARAM && k_tag == full_tag && k_uid == uid))
+ return -EINVAL;
+ free(line_buffer);
+ return 0;
+ }
+ free(line_buffer);
+ return -ENOENT;
+}
+
+void checkNoSocketPointerLeaks(int family) {
+ int sockfd = socket(family, SOCK_STREAM, 0);
+ uid_t uid = getuid();
+ int tag = arc4random();
+ int ref_cnt;
+ uint64_t sk_addr;
+ uint64_t expect_addr = 0;
+
+ EXPECT_EQ(0, qtaguid_tagSocket(sockfd, tag, uid));
+ EXPECT_EQ(0, getCtrlSkInfo(tag, uid, &sk_addr, &ref_cnt));
+ EXPECT_EQ(expect_addr, sk_addr);
+ EXPECT_EQ(0, qtaguid_untagSocket(sockfd));
+ EXPECT_EQ(-ENOENT, getCtrlSkInfo(tag, uid, &sk_addr, &ref_cnt));
+}
+#else
+void checkNoSocketPointerLeaks(int family) {}
+#endif
+
+TEST (NativeQtaguidTest, no_socket_addr_leak) {
+ checkNoSocketPointerLeaks(AF_INET);
+ checkNoSocketPointerLeaks(AF_INET6);
+}
+
+int main(int argc, char **argv) {
+ testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/tests/tests/security/res/raw/bug_21443020_webm.mp4 b/tests/tests/security/res/raw/bug_21443020_webm.mp4
new file mode 100644
index 0000000..edb761a
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_21443020_webm.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_22771132.mp4 b/tests/tests/security/res/raw/bug_22771132.mp4
new file mode 100644
index 0000000..f97e1f3
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_22771132.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
deleted file mode 100644
index 9041013..0000000
--- a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * 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.security.cts;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.security.cts.activity.ISecureRandomService;
-import android.security.cts.activity.SecureRandomService;
-import android.test.AndroidTestCase;
-
-import java.io.BufferedReader;
-import java.io.EOFException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-public class ClonedSecureRandomTest extends AndroidTestCase {
- private static final int MAX_SHUTDOWN_TRIES = 50;
-
- private static final int ANSWER_TIMEOUT_SECONDS = 180;
-
- private static final String SEPARATE_PROCESS_NAME = ":secureRandom";
-
- private static final int MAX_PID = 32768;
-
- /**
- * Attempt to burn through PIDs faster after this many iterations to reach a
- * wrap-around point faster.
- */
- private static final int PRIMING_ITERATIONS = 128;
-
- private static final int RANDOM_BYTES_PER_PID = 8;
-
- private static final int MAX_PIDS_WASTED = 1024;
-
- private static final int PID_WASTING_SKIP_LOWER = 64;
-
- private static final int PID_WASTING_SKIP_UPPER = 2048;
-
- private volatile CountDownLatch mLatch;
-
- private Intent mSeparateIntent;
-
- private ISecureRandomService mSecureRandomService;
-
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- mSecureRandomService = ISecureRandomService.Stub.asInterface(service);
- mLatch.countDown();
- }
-
- public void onServiceDisconnected(ComponentName className) {
- }
- };
-
- private boolean mHasDisconnected;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mSeparateIntent = new Intent(getContext(), SecureRandomService.class);
- }
-
- /**
- * This test spawns a Service in a new process to check the initial state of
- * SecureRandom. It then attempts to make the PID number wrap around so it
- * sees a new process with the same PID twice. The test completes when it
- * sees two newly started processes with the same PID and compares their
- * output.
- */
- public void testCheckForDuplicateOutput() throws Exception {
- assertEquals("Only supports up to " + MAX_PID + " because of memory requirements",
- Integer.toString(MAX_PID), getFirstLineFromFile("/proc/sys/kernel/pid_max"));
-
- final String packageName = getContext().getPackageName();
- String separateProcessName = packageName + SEPARATE_PROCESS_NAME;
-
- /*
- * Using a byte[][] and BitSet gives us a fixed upper bound for the
- * memory cost of this test. One could possibly use a SparseArray if the
- * upper bound becomes too large (for instance, if PID_MAX is large),
- * only keep track of a smaller number of outputs, and just cause a
- * wrap-around of PIDs to keep the test working.
- */
- byte[][] outputs = new byte[MAX_PID][RANDOM_BYTES_PER_PID];
- BitSet seenPids = new BitSet(MAX_PID);
-
- ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-
- int myPid = android.os.Process.myPid();
-
- /*
- * We're guaranteed to see at least one duplicate if we iterate MAX_PID
- * number of times because of the pigeonhole principle. In an attempt to
- * hit a collision faster, first get a closely-spaced sampling of PIDs
- * then spin up a bunch of threads locally to get us closer to wrapping
- * around to the first PID.
- */
- int firstPid = -1;
- int previousPid = -1;
- int lastPid = -1;
- for (int i = 0; i < MAX_PID; i++) {
- byte[] output = new byte[RANDOM_BYTES_PER_PID];
- int pid;
-
- mLatch = new CountDownLatch(1);
- getContext().startService(mSeparateIntent);
- getContext().bindService(mSeparateIntent, mServiceConnection, 0);
- if (!mLatch.await(ANSWER_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
- fail("Timeout waiting for answer from SecureRandomService; cannot complete test");
- }
-
- // Create another latch we'll use to ensure the service has stopped.
- final CountDownLatch serviceStopLatch = new CountDownLatch(1);
- mSecureRandomService.asBinder().linkToDeath(new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- serviceStopLatch.countDown();
- }
- }, 0);
-
- try {
- pid = mSecureRandomService.getRandomBytesAndPid(output);
- } catch (RemoteException e) {
- // The process died before we could query. Try again.
- continue;
- }
-
- getContext().unbindService(mServiceConnection);
-
- /*
- * Ensure the background process has stopped by waiting for the
- * latch to fire.
- */
- int tries = 0;
- do {
- /*
- * If this has looped more than once, try to yield to
- * system_server.
- */
- if (tries > 0) {
- Thread.yield();
- }
- getContext().stopService(mSeparateIntent);
- am.killBackgroundProcesses(packageName);
- } while (!serviceStopLatch.await(100, TimeUnit.MILLISECONDS) && tries++ < MAX_SHUTDOWN_TRIES);
- assertTrue("Background process should have stopped already", tries < MAX_SHUTDOWN_TRIES);
-
- /*
- * Make sure the AndroidManifest.xml wasn't altered in a way that
- * breaks the test.
- */
- assertFalse("SecureRandomService must run in a different process. Check "
- + "AndroidManifest.xml to ensure it has a unique android:process=\"...\"",
- myPid == pid);
-
- // We didn't get a new process for some reason. Try again.
- if (previousPid == pid) {
- i--;
- continue;
- } else if (previousPid == -1 && firstPid == -1) {
- /*
- * The first time around, we'll discard the output. This is
- * needed because we don't know if the SecureRandomService instance
- * has been running before or not. To be consistent, we only
- * want the first outputs from SecureRandom for this test.
- */
- i--;
- previousPid = pid;
- continue;
- } else {
- previousPid = pid;
- }
-
- if (seenPids.get(pid)) {
- assertFalse("SecureRandom should not output the same value twice (pid=" + pid
- + ", output=" + Arrays.toString(output) + ", outputs[pid]="
- + Arrays.toString(outputs[pid]) + ")",
- Arrays.equals(output, outputs[pid]));
- return;
- }
-
- seenPids.set(pid);
- System.arraycopy(output, 0, outputs[pid], 0, output.length);
-
- if (firstPid == -1) {
- firstPid = pid;
- }
-
- if (i <= PRIMING_ITERATIONS) {
- lastPid = pid;
- } else if (pid > lastPid && (lastPid > firstPid || pid < firstPid)) {
- wastePids(firstPid, previousPid);
- }
- }
-
- /*
- * This should never be reached unless the test was altered to break it.
- * Since we're looping until we see PID_MAX unique answers, we must have
- * seen a duplicate by the pigeonhole principle.
- */
- fail("Must see a duplicate PID");
- }
-
- /**
- * This is an attempt to get the PIDs to roll over faster. Threads use up
- * PIDs on Android and spawning a new thread is much faster than having
- * another service spawned as we are doing in this test.
- */
- private static void wastePids(int firstPid, int previousPid) {
- int distance = (firstPid - previousPid + MAX_PID) % MAX_PID;
-
- // Don't waste PIDs if we're close to wrap-around to improve odds of
- // collision.
- if ((distance < PID_WASTING_SKIP_LOWER) || (MAX_PID - distance < PID_WASTING_SKIP_UPPER)) {
- return;
- }
-
- for (int i = 0; i < distance; i++) {
- Thread t = new Thread();
- t.start();
- }
- }
-
- private static String getFirstLineFromFile(String filename) throws IOException {
- BufferedReader in = null;
- try {
- in = new BufferedReader(new FileReader(filename));
- final String line = in.readLine();
- if (line == null) {
- throw new EOFException("EOF encountered before reading first line of " + filename);
- }
- return line.trim();
- } finally {
- if (in != null) {
- in.close();
- }
- }
- }
-}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 3286280..0f6cbbd 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -45,6 +45,7 @@
import android.webkit.cts.CtsTestServer;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.locks.Condition;
@@ -70,6 +71,14 @@
before any existing test methods
***********************************************************/
+ public void testStagefright_bug_22771132() throws Exception {
+ doStagefrightTest(R.raw.bug_22771132);
+ }
+
+ public void testStagefright_bug_21443020() throws Exception {
+ doStagefrightTest(R.raw.bug_21443020_webm);
+ }
+
public void testStagefright_bug_34360591() throws Exception {
doStagefrightTest(R.raw.bug_34360591);
}
@@ -150,19 +159,23 @@
doStagefrightTest(R.raw.cve_2015_6598);
}
- public void testStagefright_bug_32873375() throws Exception {
- doStagefrightTest(R.raw.bug_32873375);
- }
-
public void testStagefright_bug_26366256() throws Exception {
doStagefrightTest(R.raw.bug_26366256);
}
+ public void testStagefright_cve_2016_2429_b_27211885() throws Exception {
+ doStagefrightTest(R.raw.cve_2016_2429_b_27211885);
+ }
+
/***********************************************************
to prevent merge conflicts, add M tests below this comment,
before any existing test methods
***********************************************************/
+ public void testStagefright_bug_32873375() throws Exception {
+ doStagefrightTest(R.raw.bug_32873375);
+ }
+
public void testStagefright_bug_33818508() throws Exception {
doStagefrightTest(R.raw.bug_33818508);
}
@@ -255,10 +268,6 @@
doStagefrightTest(R.raw.bug_27855419);
}
- public void testStagefright_cve_2016_2429_b_27211885() throws Exception {
- doStagefrightTest(R.raw.cve_2016_2429_b_27211885);
- }
-
/***********************************************************
to prevent merge conflicts, add N tests below this comment,
before any existing test methods
@@ -407,7 +416,6 @@
final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener();
-
LooperThread t = new LooperThread(new Runnable() {
@Override
public void run() {
@@ -514,7 +522,6 @@
// indicative of problems with our tame CTS test web server
}
}
- MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
int numtracks = ex.getTrackCount();
String rname = url != null ? url: resources.getResourceEntryName(rid);
Log.i(TAG, "start mediacodec test for: " + rname + ", which has " + numtracks + " tracks");
@@ -529,13 +536,15 @@
continue;
}
String mime = format.getString(MediaFormat.KEY_MIME);
- for (MediaCodecInfo info: codecList.getCodecInfos()) {
+ int numCodecs = MediaCodecList.getCodecCount();
+ for (int i = 0; i < numCodecs; i++) {
+ MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
if (info.isEncoder()) {
continue;
}
try {
MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(mime);
- if (caps != null && caps.isFormatSupported(format)) {
+ if (caps != null) {
matchingCodecs.add(info.getName());
}
} catch (IllegalArgumentException e) {
@@ -556,17 +565,22 @@
if (mime.startsWith("video/")) {
surface = getDummySurface();
}
- codec.configure(format, surface, null, 0);
- codec.start();
+ try {
+ codec.configure(format, surface, null, 0);
+ codec.start();
+ } catch (Exception e) {
+ Log.i(TAG, "Failed to start/configure:", e);
+ }
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
try {
+ ByteBuffer [] inputBuffers = codec.getInputBuffers();
while (true) {
int flags = ex.getSampleFlags();
long time = ex.getSampleTime();
ex.getCachedDuration();
int bufidx = codec.dequeueInputBuffer(5000);
if (bufidx >= 0) {
- int n = ex.readSampleData(codec.getInputBuffer(bufidx), 0);
+ int n = ex.readSampleData(inputBuffers[bufidx], 0);
if (n < 0) {
flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
time = 0;
@@ -587,14 +601,8 @@
codec.releaseOutputBuffer(status, true);
}
}
- } catch (MediaCodec.CodecException ce) {
- if (ce.getErrorCode() == MediaCodec.CodecException.ERROR_RECLAIMED) {
- // This indicates that the remote service is dead, suggesting a crash.
- throw new RuntimeException(ce);
- }
- // Other errors ignored.
- } catch (IllegalStateException ise) {
- // Other errors ignored.
+ } catch (Exception e) {
+ // local exceptions ignored, not security issues
} finally {
codec.release();
}
@@ -606,7 +614,7 @@
assertFalse("Device *IS* vulnerable to " + cve,
mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
thr.stopLooper();
-
+ thr.join();
}
private void doStagefrightTestMediaMetadataRetriever(final int rid) throws Exception {
diff --git a/tests/tests/text/src/android/text/method/cts/TouchTest.java b/tests/tests/text/src/android/text/method/cts/TouchTest.java
index 233539c..d9b8aa9 100644
--- a/tests/tests/text/src/android/text/method/cts/TouchTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TouchTest.java
@@ -27,6 +27,7 @@
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.MotionEvent;
+import android.view.ViewGroup;
import android.widget.TextView;
public class TouchTest extends ActivityInstrumentationTestCase2<CtsActivity> {
@@ -58,7 +59,11 @@
runTestOnUiThread(new Runnable() {
public void run() {
- mActivity.setContentView(tv);
+ final float pixelPerSp =
+ mActivity.getResources().getDisplayMetrics().scaledDensity;
+ // Explicitly set the width so that |LONG_TEXT| causes horizontal scroll.
+ mActivity.setContentView(tv, new ViewGroup.LayoutParams(
+ (int)(100 * pixelPerSp), ViewGroup.LayoutParams.MATCH_PARENT));
tv.setSingleLine(true);
tv.setLines(2);
}
diff --git a/tools/cts-tradefed/Android.mk b/tools/cts-tradefed/Android.mk
index 0c94c39..8fb638c 100644
--- a/tools/cts-tradefed/Android.mk
+++ b/tools/cts-tradefed/Android.mk
@@ -25,7 +25,7 @@
LOCAL_SUITE_TARGET_ARCH := $(TARGET_ARCH)
LOCAL_SUITE_NAME := CTS
LOCAL_SUITE_FULLNAME := "Compatibility Test Suite"
-LOCAL_SUITE_VERSION := 7.1_r7
+LOCAL_SUITE_VERSION := 7.1_r8
LOCAL_MODULE := cts-tradefed