1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.drm.cts; 18 19 import com.android.compatibility.common.util.MediaUtils; 20 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.content.res.AssetFileDescriptor; 24 import android.test.AndroidTestCase; 25 import android.util.Log; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.RandomAccessFile; 29 import java.io.SequenceInputStream; 30 import java.nio.charset.StandardCharsets; 31 import java.io.ByteArrayInputStream; 32 import java.io.File; 33 import java.io.FileInputStream; 34 import java.io.FileNotFoundException; 35 import java.io.FileOutputStream; 36 import java.util.HashMap; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Enumeration; 40 import java.util.Iterator; 41 import java.util.Vector; 42 43 import android.drm.DrmManagerClient; 44 import android.drm.DrmConvertedStatus; 45 import android.drm.DrmEvent; 46 import android.drm.DrmInfo; 47 import android.drm.DrmInfoRequest; 48 import android.drm.DrmInfoStatus; 49 import android.drm.DrmRights; 50 import android.drm.DrmStore; 51 import android.drm.DrmUtils; 52 import android.media.MediaExtractor; 53 import android.media.MediaMetadataRetriever; 54 import android.media.MediaPlayer; 55 import android.net.Uri; 56 import android.os.ParcelFileDescriptor; 57 import android.os.SystemClock; 58 import android.platform.test.annotations.AppModeFull; 59 60 public class DRMTest extends AndroidTestCase { 61 private static String TAG = "CtsDRMTest"; 62 private static final int WAIT_TIME = 60000; // 1 min max 63 64 private Object mLock = new Object(); 65 private ArrayList<Config> mConfigs = new ArrayList<Config>(); 66 private DrmRights mDrmRights; 67 private DrmManagerClient mDrmManagerClient; 68 69 @Override setUp()70 protected void setUp() throws Exception { 71 super.setUp(); 72 mDrmManagerClient = new DrmManagerClient(getContext()); 73 String[] plugins = mDrmManagerClient.getAvailableDrmEngines(); 74 75 mConfigs.clear(); 76 for(String plugInName : plugins) { 77 Config config = ConfigFactory.getConfig(plugInName); 78 if (null != config) { 79 mConfigs.add(config); 80 } 81 } 82 } 83 register(Config config)84 private void register(Config config) throws Exception { 85 DrmInfo drmInfo = executeAcquireDrmInfo(DrmInfoRequest.TYPE_REGISTRATION_INFO, 86 config.getInfoOfRegistration(), 87 config.getMimeType()); 88 executeProcessDrmInfo(drmInfo, config); 89 } 90 acquireRights(Config config)91 private void acquireRights(Config config) throws Exception { 92 DrmInfo drmInfo = executeAcquireDrmInfo(DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO, 93 config.getInfoOfRightsAcquisition(), 94 config.getMimeType()); 95 executeProcessDrmInfo(drmInfo, config); 96 } 97 deregister(Config config)98 private void deregister(Config config) throws Exception { 99 DrmInfo drmInfo = executeAcquireDrmInfo(DrmInfoRequest.TYPE_UNREGISTRATION_INFO, 100 config.getInfoOfRegistration(), 101 config.getMimeType()); 102 executeProcessDrmInfo(drmInfo, config); 103 } 104 testIsDrmDirectoryExist()105 public void testIsDrmDirectoryExist() { 106 assertTrue("/data/drm/ does not exist", new File("/data/drm/").exists()); 107 } 108 testRegisterAndDeregister()109 public void testRegisterAndDeregister() throws Exception { 110 for (Config config : mConfigs) { 111 register(config); 112 deregister(config); 113 } 114 } 115 testAcquireRights()116 public void testAcquireRights() throws Exception { 117 for (Config config : mConfigs) { 118 register(config); 119 acquireRights(config); 120 deregister(config); 121 } 122 } 123 testGetConstraints()124 public void testGetConstraints() throws Exception { 125 for (Config config : mConfigs) { 126 register(config); 127 acquireRights(config); 128 ContentValues constraints = mDrmManagerClient.getConstraints( 129 config.getContentPath(), 130 DrmStore.Action.DEFAULT); 131 assertNotNull("Failed on plugin: " + config.getPluginName(), constraints); 132 deregister(config); 133 } 134 } 135 testSupportsHttps()136 public void testSupportsHttps() throws Exception { 137 mDrmManagerClient.getConstraints(Uri.parse("https://www.foo.com"), 138 DrmStore.Action.DEFAULT); 139 } 140 testCanHandle()141 public void testCanHandle() throws Exception { 142 for (Config config : mConfigs) { 143 assertTrue("Failed on plugin: " + config.getPluginName(), 144 mDrmManagerClient.canHandle(config.getContentPath(), config.getMimeType())); 145 } 146 } 147 testGetOriginalMimeType()148 public void testGetOriginalMimeType() throws Exception { 149 for (Config config : mConfigs) { 150 assertNotNull("Failed on plugin: " + config.getPluginName(), 151 mDrmManagerClient.getOriginalMimeType(config.getContentPath())); 152 } 153 } 154 testCheckRightsStatus()155 public void testCheckRightsStatus() throws Exception { 156 for (Config config : mConfigs) { 157 register(config); 158 acquireRights(config); 159 int rightsStatus = mDrmManagerClient.checkRightsStatus( 160 config.getContentPath(), 161 DrmStore.Action.PLAY); 162 assertEquals("Failed on plugin: " + config.getPluginName(), 163 DrmStore.RightsStatus.RIGHTS_VALID, rightsStatus); 164 deregister(config); 165 } 166 } 167 testRemoveRights()168 public void testRemoveRights() throws Exception { 169 for (Config config : mConfigs) { 170 assertEquals("Failed on plugin: " + config.getPluginName(), 171 DrmManagerClient.ERROR_NONE, 172 mDrmManagerClient.removeRights(config.getContentPath())); 173 } 174 } 175 testRemoveAllRights()176 public void testRemoveAllRights() throws Exception { 177 for (Config config : mConfigs) { 178 assertEquals("Failed on plugin: " + config.getPluginName(), 179 mDrmManagerClient.removeAllRights(), DrmManagerClient.ERROR_NONE); 180 } 181 } 182 testConvertData()183 public void testConvertData() throws Exception { 184 for (Config config : mConfigs) { 185 byte[] inputData = new byte[]{'T','E','S','T'}; 186 187 int convertId = mDrmManagerClient.openConvertSession(config.getMimeType()); 188 DrmConvertedStatus drmConvertStatus 189 = mDrmManagerClient.convertData(convertId, inputData); 190 mDrmManagerClient.closeConvertSession(convertId); 191 } 192 } 193 executeAcquireDrmInfo( int type, HashMap<String, String> request, String mimeType)194 private DrmInfo executeAcquireDrmInfo( 195 int type, HashMap<String, String> request, String mimeType) throws Exception { 196 DrmInfoRequest infoRequest = new DrmInfoRequest(type, mimeType); 197 198 for (Iterator it = request.keySet().iterator(); it.hasNext(); ) { 199 String key = (String) it.next(); 200 String value = request.get(key); 201 infoRequest.put(key, value); 202 } 203 204 return mDrmManagerClient.acquireDrmInfo(infoRequest); 205 } 206 executeProcessDrmInfo(DrmInfo drmInfo, Config config)207 private void executeProcessDrmInfo(DrmInfo drmInfo, Config config) throws Exception { 208 if (drmInfo == null) { 209 return; 210 } 211 212 mDrmManagerClient.setOnEventListener(new OnEventListenerImpl(config)); 213 drmInfo.put(DrmInfoRequest.ACCOUNT_ID, config.getAccountId()); 214 assertEquals("Failed on plugin: " + config.getPluginName(), 215 DrmManagerClient.ERROR_NONE, mDrmManagerClient.processDrmInfo(drmInfo)); 216 217 synchronized(mLock) { 218 try { 219 mLock.wait(WAIT_TIME); 220 } catch(Exception e) { 221 Log.v(TAG, "ProcessDrmInfo: wait was interrupted."); 222 } 223 } 224 } 225 226 @AppModeFull(reason = "Instant apps cannot hold READ/WRITE_EXTERNAL_STORAGE") testForwardLockAccess()227 public void testForwardLockAccess() throws Exception { 228 DrmManagerClient drmManager= new DrmManagerClient(mContext); 229 String[] engines = drmManager.getAvailableDrmEngines(); 230 boolean haveForwardLock = false; 231 for (String engine: engines) { 232 if (engine.equals("OMA V1 Forward Lock")) { 233 haveForwardLock = true; 234 } 235 } 236 drmManager.close(); 237 if (!haveForwardLock) { 238 Log.i(TAG, "Skipping forward lock test because forward lock is not available"); 239 return; 240 } 241 242 Vector<InputStream> sequence = new Vector<InputStream>(); 243 244 String dmHeader = "--mime_content_boundary\r\n" + 245 "Content-Type: audio/mpeg\r\n" + 246 "Content-Transfer-Encoding: binary\r\n\r\n"; 247 sequence.add(new ByteArrayInputStream(dmHeader.getBytes(StandardCharsets.UTF_8))); 248 249 AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(R.raw.testmp3_2); 250 FileInputStream body = afd.createInputStream(); 251 sequence.add(body); 252 253 String dmFooter = "\r\n--mime_content_boundary--"; 254 sequence.add(new ByteArrayInputStream(dmFooter.getBytes(StandardCharsets.UTF_8))); 255 256 SequenceInputStream dmStream = new SequenceInputStream(sequence.elements()); 257 String flPath = mContext.getExternalCacheDir() + "/temp.fl"; 258 RandomAccessFile flFile = new RandomAccessFile(flPath, "rw"); 259 assertTrue("couldn't convert to fl file", 260 MediaUtils.convertDmToFl(mContext, dmStream, flFile)); 261 dmStream.close(); // this closes the underlying streams and AFD as well 262 flFile.close(); 263 264 ParcelFileDescriptor flFd = null; 265 try { 266 // check that the .fl file can be played 267 MediaPlayer player = new MediaPlayer(); 268 try { 269 flFd = ParcelFileDescriptor.open( 270 new File(flPath), ParcelFileDescriptor.MODE_READ_ONLY); 271 player.setDataSource(flFd.getFileDescriptor(), 0, flFd.getStatSize()); 272 player.prepare(); 273 player.start(); 274 SystemClock.sleep(2000); 275 assertTrue("player is not playing", player.isPlaying()); 276 player.release(); 277 } catch (Exception e) { 278 Log.d(TAG, "MediaPlayer playback failed:", e); 279 } finally { 280 player.release(); 281 } 282 283 // check that the .fl file can be parsed with MediaMetadataRetriever 284 MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 285 try { 286 retriever.setDataSource(flFd.getFileDescriptor()); 287 String numTracks = 288 retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS); 289 assertEquals("wrong number of tracks found in file", "1", numTracks); 290 } finally { 291 retriever.release(); 292 } 293 294 // check that the .fl file cannot be opened with MediaExtractor 295 MediaExtractor ex = new MediaExtractor(); 296 try { 297 ex.setDataSource(flFd.getFileDescriptor()); 298 int n = ex.getTrackCount(); 299 fail("extractor creation should have failed, but found " + n + " tracks"); 300 } catch (Exception e) { 301 // ignore, expected to fail 302 } finally { 303 ex.release(); 304 } 305 } finally { 306 flFd.close(); 307 new File(flPath).delete(); 308 } 309 } 310 311 private class OnEventListenerImpl implements DrmManagerClient.OnEventListener { 312 private Config mConfig; OnEventListenerImpl(Config config)313 public OnEventListenerImpl(Config config) { 314 mConfig = config; 315 } 316 317 @Override onEvent(DrmManagerClient client, DrmEvent event)318 public void onEvent(DrmManagerClient client, DrmEvent event) { 319 switch (event.getType()) { 320 case DrmEvent.TYPE_DRM_INFO_PROCESSED: 321 Log.d(TAG, "processDrmInfo() completed"); 322 DrmInfoStatus infoStatus 323 = (DrmInfoStatus) event.getAttribute(DrmEvent.DRM_INFO_STATUS_OBJECT); 324 switch (infoStatus.infoType) { 325 case DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO: 326 mDrmRights = new DrmRights(infoStatus.data, infoStatus.mimeType); 327 assertNotNull(mDrmRights); 328 try { 329 assertEquals(DrmManagerClient.ERROR_NONE, mDrmManagerClient.saveRights( 330 mDrmRights, mConfig.getRightsPath(), mConfig.getContentPath())); 331 Log.d(TAG, "Rights saved"); 332 } catch (IOException e) { 333 Log.e(TAG, "Save Rights failed"); 334 e.printStackTrace(); 335 } 336 break; 337 case DrmInfoRequest.TYPE_REGISTRATION_INFO: 338 Log.d(TAG, "Registration completed"); 339 break; 340 case DrmInfoRequest.TYPE_UNREGISTRATION_INFO: 341 Log.d(TAG, "Deregistration completed"); 342 break; 343 default: 344 break; 345 } 346 break; 347 default: 348 break; 349 } 350 synchronized (mLock) { 351 mLock.notify(); 352 } 353 } 354 } 355 } 356