1 /* 2 * Copyright (C) 2010 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; 18 19 import android.annotation.NonNull; 20 import android.content.ContentResolver; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.database.Cursor; 24 import android.database.sqlite.SQLiteException; 25 import android.net.Uri; 26 import android.os.Handler; 27 import android.os.HandlerThread; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.provider.MediaStore; 31 import android.util.Log; 32 33 import dalvik.system.CloseGuard; 34 35 import java.io.File; 36 import java.io.FileDescriptor; 37 import java.io.FileInputStream; 38 import java.io.IOException; 39 import java.lang.ref.WeakReference; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collection; 43 import java.util.HashMap; 44 import java.util.concurrent.atomic.AtomicBoolean; 45 46 /** 47 * The main programming interface for the DRM framework. An application must instantiate this class 48 * to access DRM agents through the DRM framework. 49 * 50 * @deprecated Please use {@link android.media.MediaDrm} 51 */ 52 @Deprecated 53 public class DrmManagerClient implements AutoCloseable { 54 /** 55 * Indicates that a request was successful or that no error occurred. 56 */ 57 public static final int ERROR_NONE = 0; 58 /** 59 * Indicates that an error occurred and the reason is not known. 60 */ 61 public static final int ERROR_UNKNOWN = -2000; 62 63 /** {@hide} */ 64 public static final int INVALID_SESSION = -1; 65 66 HandlerThread mInfoThread; 67 HandlerThread mEventThread; 68 private static final String TAG = "DrmManagerClient"; 69 70 private final AtomicBoolean mClosed = new AtomicBoolean(); 71 private final CloseGuard mCloseGuard = CloseGuard.get(); 72 73 static { 74 // Load the respective library 75 System.loadLibrary("drmframework_jni"); 76 } 77 78 /** 79 * Interface definition for a callback that receives status messages and warnings 80 * during registration and rights acquisition. 81 */ 82 public interface OnInfoListener { 83 /** 84 * Called when the DRM framework sends status or warning information during registration 85 * and rights acquisition. 86 * 87 * @param client The <code>DrmManagerClient</code> instance. 88 * @param event The {@link DrmInfoEvent} instance that wraps the status information or 89 * warnings. 90 */ onInfo(DrmManagerClient client, DrmInfoEvent event)91 public void onInfo(DrmManagerClient client, DrmInfoEvent event); 92 } 93 94 /** 95 * Interface definition for a callback that receives information 96 * about DRM processing events. 97 */ 98 public interface OnEventListener { 99 /** 100 * Called when the DRM framework sends information about a DRM processing request. 101 * 102 * @param client The <code>DrmManagerClient</code> instance. 103 * @param event The {@link DrmEvent} instance that wraps the information being 104 * conveyed, such as the information type and message. 105 */ onEvent(DrmManagerClient client, DrmEvent event)106 public void onEvent(DrmManagerClient client, DrmEvent event); 107 } 108 109 /** 110 * Interface definition for a callback that receives information about DRM framework errors. 111 */ 112 public interface OnErrorListener { 113 /** 114 * Called when the DRM framework sends error information. 115 * 116 * @param client The <code>DrmManagerClient</code> instance. 117 * @param event The {@link DrmErrorEvent} instance that wraps the error type and message. 118 */ onError(DrmManagerClient client, DrmErrorEvent event)119 public void onError(DrmManagerClient client, DrmErrorEvent event); 120 } 121 122 private static final int ACTION_REMOVE_ALL_RIGHTS = 1001; 123 private static final int ACTION_PROCESS_DRM_INFO = 1002; 124 125 private int mUniqueId; 126 private long mNativeContext; 127 private Context mContext; 128 private InfoHandler mInfoHandler; 129 private EventHandler mEventHandler; 130 private OnInfoListener mOnInfoListener; 131 private OnEventListener mOnEventListener; 132 private OnErrorListener mOnErrorListener; 133 134 private class EventHandler extends Handler { 135 EventHandler(Looper looper)136 public EventHandler(Looper looper) { 137 super(looper); 138 } 139 handleMessage(Message msg)140 public void handleMessage(Message msg) { 141 DrmEvent event = null; 142 DrmErrorEvent error = null; 143 HashMap<String, Object> attributes = new HashMap<String, Object>(); 144 145 switch(msg.what) { 146 case ACTION_PROCESS_DRM_INFO: { 147 final DrmInfo drmInfo = (DrmInfo) msg.obj; 148 DrmInfoStatus status = _processDrmInfo(mUniqueId, drmInfo); 149 150 attributes.put(DrmEvent.DRM_INFO_STATUS_OBJECT, status); 151 attributes.put(DrmEvent.DRM_INFO_OBJECT, drmInfo); 152 153 if (null != status && DrmInfoStatus.STATUS_OK == status.statusCode) { 154 event = new DrmEvent(mUniqueId, 155 getEventType(status.infoType), null, attributes); 156 } else { 157 int infoType = (null != status) ? status.infoType : drmInfo.getInfoType(); 158 error = new DrmErrorEvent(mUniqueId, 159 getErrorType(infoType), null, attributes); 160 } 161 break; 162 } 163 case ACTION_REMOVE_ALL_RIGHTS: { 164 if (ERROR_NONE == _removeAllRights(mUniqueId)) { 165 event = new DrmEvent(mUniqueId, DrmEvent.TYPE_ALL_RIGHTS_REMOVED, null); 166 } else { 167 error = new DrmErrorEvent(mUniqueId, 168 DrmErrorEvent.TYPE_REMOVE_ALL_RIGHTS_FAILED, null); 169 } 170 break; 171 } 172 default: 173 Log.e(TAG, "Unknown message type " + msg.what); 174 return; 175 } 176 if (null != mOnEventListener && null != event) { 177 mOnEventListener.onEvent(DrmManagerClient.this, event); 178 } 179 if (null != mOnErrorListener && null != error) { 180 mOnErrorListener.onError(DrmManagerClient.this, error); 181 } 182 } 183 } 184 185 /** 186 * {@hide} 187 */ notify( Object thisReference, int uniqueId, int infoType, String message)188 public static void notify( 189 Object thisReference, int uniqueId, int infoType, String message) { 190 DrmManagerClient instance = (DrmManagerClient)((WeakReference)thisReference).get(); 191 192 if (null != instance && null != instance.mInfoHandler) { 193 Message m = instance.mInfoHandler.obtainMessage( 194 InfoHandler.INFO_EVENT_TYPE, uniqueId, infoType, message); 195 instance.mInfoHandler.sendMessage(m); 196 } 197 } 198 199 private class InfoHandler extends Handler { 200 public static final int INFO_EVENT_TYPE = 1; 201 InfoHandler(Looper looper)202 public InfoHandler(Looper looper) { 203 super(looper); 204 } 205 handleMessage(Message msg)206 public void handleMessage(Message msg) { 207 DrmInfoEvent info = null; 208 DrmErrorEvent error = null; 209 210 switch (msg.what) { 211 case InfoHandler.INFO_EVENT_TYPE: 212 int uniqueId = msg.arg1; 213 int infoType = msg.arg2; 214 String message = msg.obj.toString(); 215 216 switch (infoType) { 217 case DrmInfoEvent.TYPE_REMOVE_RIGHTS: { 218 try { 219 DrmUtils.removeFile(message); 220 } catch (IOException e) { 221 e.printStackTrace(); 222 } 223 info = new DrmInfoEvent(uniqueId, infoType, message); 224 break; 225 } 226 case DrmInfoEvent.TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT: 227 case DrmInfoEvent.TYPE_RIGHTS_INSTALLED: 228 case DrmInfoEvent.TYPE_WAIT_FOR_RIGHTS: 229 case DrmInfoEvent.TYPE_ACCOUNT_ALREADY_REGISTERED: 230 case DrmInfoEvent.TYPE_RIGHTS_REMOVED: { 231 info = new DrmInfoEvent(uniqueId, infoType, message); 232 break; 233 } 234 default: 235 error = new DrmErrorEvent(uniqueId, infoType, message); 236 break; 237 } 238 239 if (null != mOnInfoListener && null != info) { 240 mOnInfoListener.onInfo(DrmManagerClient.this, info); 241 } 242 if (null != mOnErrorListener && null != error) { 243 mOnErrorListener.onError(DrmManagerClient.this, error); 244 } 245 return; 246 default: 247 Log.e(TAG, "Unknown message type " + msg.what); 248 return; 249 } 250 } 251 } 252 253 /** 254 * Creates a <code>DrmManagerClient</code>. 255 * 256 * @param context Context of the caller. 257 */ DrmManagerClient(Context context)258 public DrmManagerClient(Context context) { 259 mContext = context; 260 createEventThreads(); 261 262 // save the unique id 263 mUniqueId = _initialize(); 264 mCloseGuard.open("release"); 265 } 266 267 @Override finalize()268 protected void finalize() throws Throwable { 269 try { 270 if (mCloseGuard != null) { 271 mCloseGuard.warnIfOpen(); 272 } 273 274 close(); 275 } finally { 276 super.finalize(); 277 } 278 } 279 280 /** 281 * Releases resources associated with the current session of 282 * DrmManagerClient. It is considered good practice to call this method when 283 * the {@link DrmManagerClient} object is no longer needed in your 284 * application. After this method is called, {@link DrmManagerClient} is no 285 * longer usable since it has lost all of its required resource. 286 * 287 * This method was added in API 24. In API versions 16 through 23, release() 288 * should be called instead. There is no need to do anything for API 289 * versions prior to 16. 290 */ 291 @Override close()292 public void close() { 293 mCloseGuard.close(); 294 if (mClosed.compareAndSet(false, true)) { 295 if (mEventHandler != null) { 296 mEventThread.quit(); 297 mEventThread = null; 298 } 299 if (mInfoHandler != null) { 300 mInfoThread.quit(); 301 mInfoThread = null; 302 } 303 mEventHandler = null; 304 mInfoHandler = null; 305 mOnEventListener = null; 306 mOnInfoListener = null; 307 mOnErrorListener = null; 308 _release(mUniqueId); 309 } 310 } 311 312 /** 313 * @deprecated replaced by {@link #close()}. 314 */ 315 @Deprecated release()316 public void release() { 317 close(); 318 } 319 320 /** 321 * Registers an {@link DrmManagerClient.OnInfoListener} callback, which is invoked when the 322 * DRM framework sends status or warning information during registration or rights acquisition. 323 * 324 * @param infoListener Interface definition for the callback. 325 */ setOnInfoListener(OnInfoListener infoListener)326 public synchronized void setOnInfoListener(OnInfoListener infoListener) { 327 mOnInfoListener = infoListener; 328 if (null != infoListener) { 329 createListeners(); 330 } 331 } 332 333 /** 334 * Registers an {@link DrmManagerClient.OnEventListener} callback, which is invoked when the 335 * DRM framework sends information about DRM processing. 336 * 337 * @param eventListener Interface definition for the callback. 338 */ setOnEventListener(OnEventListener eventListener)339 public synchronized void setOnEventListener(OnEventListener eventListener) { 340 mOnEventListener = eventListener; 341 if (null != eventListener) { 342 createListeners(); 343 } 344 } 345 346 /** 347 * Registers an {@link DrmManagerClient.OnErrorListener} callback, which is invoked when 348 * the DRM framework sends error information. 349 * 350 * @param errorListener Interface definition for the callback. 351 */ setOnErrorListener(OnErrorListener errorListener)352 public synchronized void setOnErrorListener(OnErrorListener errorListener) { 353 mOnErrorListener = errorListener; 354 if (null != errorListener) { 355 createListeners(); 356 } 357 } 358 359 /** 360 * Retrieves information about all the DRM plug-ins (agents) that are registered with 361 * the DRM framework. 362 * 363 * @return A <code>String</code> array of DRM plug-in descriptions. 364 */ getAvailableDrmEngines()365 public String[] getAvailableDrmEngines() { 366 DrmSupportInfo[] supportInfos = _getAllSupportInfo(mUniqueId); 367 ArrayList<String> descriptions = new ArrayList<String>(); 368 369 for (int i = 0; i < supportInfos.length; i++) { 370 descriptions.add(supportInfos[i].getDescriprition()); 371 } 372 373 String[] drmEngines = new String[descriptions.size()]; 374 return descriptions.toArray(drmEngines); 375 } 376 377 /** 378 * Retrieves information about all the DRM plug-ins (agents) that are 379 * registered with the DRM framework. 380 * 381 * @return List of all the DRM plug-ins (agents) that are registered with 382 * the DRM framework. 383 */ getAvailableDrmSupportInfo()384 public @NonNull Collection<DrmSupportInfo> getAvailableDrmSupportInfo() { 385 return Arrays.asList(_getAllSupportInfo(mUniqueId)); 386 } 387 388 /** 389 * Retrieves constraint information for rights-protected content. 390 * 391 * @param path Path to the content from which you are retrieving DRM constraints. 392 * @param action Action defined in {@link DrmStore.Action}. 393 * 394 * @return A {@link android.content.ContentValues} instance that contains 395 * key-value pairs representing the constraints. Null in case of failure. 396 * The keys are defined in {@link DrmStore.ConstraintsColumns}. 397 */ getConstraints(String path, int action)398 public ContentValues getConstraints(String path, int action) { 399 if (null == path || path.equals("") || !DrmStore.Action.isValid(action)) { 400 throw new IllegalArgumentException("Given usage or path is invalid/null"); 401 } 402 return _getConstraints(mUniqueId, path, action); 403 } 404 405 /** 406 * Retrieves metadata information for rights-protected content. 407 * 408 * @param path Path to the content from which you are retrieving metadata information. 409 * 410 * @return A {@link android.content.ContentValues} instance that contains 411 * key-value pairs representing the metadata. Null in case of failure. 412 */ getMetadata(String path)413 public ContentValues getMetadata(String path) { 414 if (null == path || path.equals("")) { 415 throw new IllegalArgumentException("Given path is invalid/null"); 416 } 417 return _getMetadata(mUniqueId, path); 418 } 419 420 /** 421 * Retrieves constraint information for rights-protected content. 422 * 423 * @param uri URI for the content from which you are retrieving DRM constraints. 424 * @param action Action defined in {@link DrmStore.Action}. 425 * 426 * @return A {@link android.content.ContentValues} instance that contains 427 * key-value pairs representing the constraints. Null in case of failure. 428 */ getConstraints(Uri uri, int action)429 public ContentValues getConstraints(Uri uri, int action) { 430 if (null == uri || Uri.EMPTY == uri) { 431 throw new IllegalArgumentException("Uri should be non null"); 432 } 433 return getConstraints(convertUriToPath(uri), action); 434 } 435 436 /** 437 * Retrieves metadata information for rights-protected content. 438 * 439 * @param uri URI for the content from which you are retrieving metadata information. 440 * 441 * @return A {@link android.content.ContentValues} instance that contains 442 * key-value pairs representing the constraints. Null in case of failure. 443 */ getMetadata(Uri uri)444 public ContentValues getMetadata(Uri uri) { 445 if (null == uri || Uri.EMPTY == uri) { 446 throw new IllegalArgumentException("Uri should be non null"); 447 } 448 return getMetadata(convertUriToPath(uri)); 449 } 450 451 /** 452 * Saves rights to a specified path and associates that path with the content path. 453 * 454 * <p class="note"><strong>Note:</strong> For OMA or WM-DRM, <code>rightsPath</code> and 455 * <code>contentPath</code> can be null.</p> 456 * 457 * @param drmRights The {@link DrmRights} to be saved. 458 * @param rightsPath File path where rights will be saved. 459 * @param contentPath File path where content is saved. 460 * 461 * @return ERROR_NONE for success; ERROR_UNKNOWN for failure. 462 * 463 * @throws IOException If the call failed to save rights information at the given 464 * <code>rightsPath</code>. 465 */ saveRights( DrmRights drmRights, String rightsPath, String contentPath)466 public int saveRights( 467 DrmRights drmRights, String rightsPath, String contentPath) throws IOException { 468 if (null == drmRights || !drmRights.isValid()) { 469 throw new IllegalArgumentException("Given drmRights or contentPath is not valid"); 470 } 471 if (null != rightsPath && !rightsPath.equals("")) { 472 DrmUtils.writeToFile(rightsPath, drmRights.getData()); 473 } 474 return _saveRights(mUniqueId, drmRights, rightsPath, contentPath); 475 } 476 477 /** 478 * Installs a new DRM plug-in (agent) at runtime. 479 * 480 * @param engineFilePath File path to the plug-in file to be installed. 481 * 482 * {@hide} 483 */ installDrmEngine(String engineFilePath)484 public void installDrmEngine(String engineFilePath) { 485 if (null == engineFilePath || engineFilePath.equals("")) { 486 throw new IllegalArgumentException( 487 "Given engineFilePath: "+ engineFilePath + "is not valid"); 488 } 489 _installDrmEngine(mUniqueId, engineFilePath); 490 } 491 492 /** 493 * Checks whether the given MIME type or path can be handled. 494 * 495 * @param path Path of the content to be handled. 496 * @param mimeType MIME type of the object to be handled. 497 * 498 * @return True if the given MIME type or path can be handled; false if they cannot be handled. 499 */ canHandle(String path, String mimeType)500 public boolean canHandle(String path, String mimeType) { 501 if ((null == path || path.equals("")) && (null == mimeType || mimeType.equals(""))) { 502 throw new IllegalArgumentException("Path or the mimetype should be non null"); 503 } 504 return _canHandle(mUniqueId, path, mimeType); 505 } 506 507 /** 508 * Checks whether the given MIME type or URI can be handled. 509 * 510 * @param uri URI for the content to be handled. 511 * @param mimeType MIME type of the object to be handled 512 * 513 * @return True if the given MIME type or URI can be handled; false if they cannot be handled. 514 */ canHandle(Uri uri, String mimeType)515 public boolean canHandle(Uri uri, String mimeType) { 516 if ((null == uri || Uri.EMPTY == uri) && (null == mimeType || mimeType.equals(""))) { 517 throw new IllegalArgumentException("Uri or the mimetype should be non null"); 518 } 519 return canHandle(convertUriToPath(uri), mimeType); 520 } 521 522 /** 523 * Processes the given DRM information based on the information type. 524 * 525 * @param drmInfo The {@link DrmInfo} to be processed. 526 * @return ERROR_NONE for success; ERROR_UNKNOWN for failure. 527 */ processDrmInfo(DrmInfo drmInfo)528 public int processDrmInfo(DrmInfo drmInfo) { 529 if (null == drmInfo || !drmInfo.isValid()) { 530 throw new IllegalArgumentException("Given drmInfo is invalid/null"); 531 } 532 int result = ERROR_UNKNOWN; 533 if (null != mEventHandler) { 534 Message msg = mEventHandler.obtainMessage(ACTION_PROCESS_DRM_INFO, drmInfo); 535 result = (mEventHandler.sendMessage(msg)) ? ERROR_NONE : result; 536 } 537 return result; 538 } 539 540 /** 541 * Retrieves information for registering, unregistering, or acquiring rights. 542 * 543 * @param drmInfoRequest The {@link DrmInfoRequest} that specifies the type of DRM 544 * information being retrieved. 545 * 546 * @return A {@link DrmInfo} instance. 547 */ acquireDrmInfo(DrmInfoRequest drmInfoRequest)548 public DrmInfo acquireDrmInfo(DrmInfoRequest drmInfoRequest) { 549 if (null == drmInfoRequest || !drmInfoRequest.isValid()) { 550 throw new IllegalArgumentException("Given drmInfoRequest is invalid/null"); 551 } 552 return _acquireDrmInfo(mUniqueId, drmInfoRequest); 553 } 554 555 /** 556 * Processes a given {@link DrmInfoRequest} and returns the rights information asynchronously. 557 *<p> 558 * This is a utility method that consists of an 559 * {@link #acquireDrmInfo(DrmInfoRequest) acquireDrmInfo()} and a 560 * {@link #processDrmInfo(DrmInfo) processDrmInfo()} method call. This utility method can be 561 * used only if the selected DRM plug-in (agent) supports this sequence of calls. Some DRM 562 * agents, such as OMA, do not support this utility method, in which case an application must 563 * invoke {@link #acquireDrmInfo(DrmInfoRequest) acquireDrmInfo()} and 564 * {@link #processDrmInfo(DrmInfo) processDrmInfo()} separately. 565 * 566 * @param drmInfoRequest The {@link DrmInfoRequest} used to acquire the rights. 567 * @return ERROR_NONE for success; ERROR_UNKNOWN for failure. 568 */ acquireRights(DrmInfoRequest drmInfoRequest)569 public int acquireRights(DrmInfoRequest drmInfoRequest) { 570 DrmInfo drmInfo = acquireDrmInfo(drmInfoRequest); 571 if (null == drmInfo) { 572 return ERROR_UNKNOWN; 573 } 574 return processDrmInfo(drmInfo); 575 } 576 577 /** 578 * Retrieves the type of rights-protected object (for example, content object, rights 579 * object, and so on) using the specified path or MIME type. At least one parameter must 580 * be specified to retrieve the DRM object type. 581 * 582 * @param path Path to the content or null. 583 * @param mimeType MIME type of the content or null. 584 * 585 * @return An <code>int</code> that corresponds to a {@link DrmStore.DrmObjectType}. 586 */ getDrmObjectType(String path, String mimeType)587 public int getDrmObjectType(String path, String mimeType) { 588 if ((null == path || path.equals("")) && (null == mimeType || mimeType.equals(""))) { 589 throw new IllegalArgumentException("Path or the mimetype should be non null"); 590 } 591 return _getDrmObjectType(mUniqueId, path, mimeType); 592 } 593 594 /** 595 * Retrieves the type of rights-protected object (for example, content object, rights 596 * object, and so on) using the specified URI or MIME type. At least one parameter must 597 * be specified to retrieve the DRM object type. 598 * 599 * @param uri URI for the content or null. 600 * @param mimeType MIME type of the content or null. 601 * 602 * @return An <code>int</code> that corresponds to a {@link DrmStore.DrmObjectType}. 603 */ getDrmObjectType(Uri uri, String mimeType)604 public int getDrmObjectType(Uri uri, String mimeType) { 605 if ((null == uri || Uri.EMPTY == uri) && (null == mimeType || mimeType.equals(""))) { 606 throw new IllegalArgumentException("Uri or the mimetype should be non null"); 607 } 608 String path = ""; 609 try { 610 path = convertUriToPath(uri); 611 } catch (Exception e) { 612 // Even uri is invalid the mimetype shall be valid, so allow to proceed further. 613 Log.w(TAG, "Given Uri could not be found in media store"); 614 } 615 return getDrmObjectType(path, mimeType); 616 } 617 618 /** 619 * Retrieves the MIME type embedded in the original content. 620 * 621 * @param path Path to the rights-protected content. 622 * 623 * @return The MIME type of the original content, such as <code>video/mpeg</code>. 624 */ getOriginalMimeType(String path)625 public String getOriginalMimeType(String path) { 626 if (null == path || path.equals("")) { 627 throw new IllegalArgumentException("Given path should be non null"); 628 } 629 630 String mime = null; 631 632 FileInputStream is = null; 633 try { 634 FileDescriptor fd = null; 635 File file = new File(path); 636 if (file.exists()) { 637 is = new FileInputStream(file); 638 fd = is.getFD(); 639 } 640 mime = _getOriginalMimeType(mUniqueId, path, fd); 641 } catch (IOException ioe) { 642 } finally { 643 if (is != null) { 644 try { 645 is.close(); 646 } catch(IOException e) {} 647 } 648 } 649 650 return mime; 651 } 652 653 /** 654 * Retrieves the MIME type embedded in the original content. 655 * 656 * @param uri URI of the rights-protected content. 657 * 658 * @return MIME type of the original content, such as <code>video/mpeg</code>. 659 */ getOriginalMimeType(Uri uri)660 public String getOriginalMimeType(Uri uri) { 661 if (null == uri || Uri.EMPTY == uri) { 662 throw new IllegalArgumentException("Given uri is not valid"); 663 } 664 return getOriginalMimeType(convertUriToPath(uri)); 665 } 666 667 /** 668 * Checks whether the given content has valid rights. 669 * 670 * @param path Path to the rights-protected content. 671 * 672 * @return An <code>int</code> representing the {@link DrmStore.RightsStatus} of the content. 673 */ checkRightsStatus(String path)674 public int checkRightsStatus(String path) { 675 return checkRightsStatus(path, DrmStore.Action.DEFAULT); 676 } 677 678 /** 679 * Check whether the given content has valid rights. 680 * 681 * @param uri URI of the rights-protected content. 682 * 683 * @return An <code>int</code> representing the {@link DrmStore.RightsStatus} of the content. 684 */ checkRightsStatus(Uri uri)685 public int checkRightsStatus(Uri uri) { 686 if (null == uri || Uri.EMPTY == uri) { 687 throw new IllegalArgumentException("Given uri is not valid"); 688 } 689 return checkRightsStatus(convertUriToPath(uri)); 690 } 691 692 /** 693 * Checks whether the given rights-protected content has valid rights for the specified 694 * {@link DrmStore.Action}. 695 * 696 * @param path Path to the rights-protected content. 697 * @param action The {@link DrmStore.Action} to perform. 698 * 699 * @return An <code>int</code> representing the {@link DrmStore.RightsStatus} of the content. 700 */ checkRightsStatus(String path, int action)701 public int checkRightsStatus(String path, int action) { 702 if (null == path || path.equals("") || !DrmStore.Action.isValid(action)) { 703 throw new IllegalArgumentException("Given path or action is not valid"); 704 } 705 return _checkRightsStatus(mUniqueId, path, action); 706 } 707 708 /** 709 * Checks whether the given rights-protected content has valid rights for the specified 710 * {@link DrmStore.Action}. 711 * 712 * @param uri URI for the rights-protected content. 713 * @param action The {@link DrmStore.Action} to perform. 714 * 715 * @return An <code>int</code> representing the {@link DrmStore.RightsStatus} of the content. 716 */ checkRightsStatus(Uri uri, int action)717 public int checkRightsStatus(Uri uri, int action) { 718 if (null == uri || Uri.EMPTY == uri) { 719 throw new IllegalArgumentException("Given uri is not valid"); 720 } 721 return checkRightsStatus(convertUriToPath(uri), action); 722 } 723 724 /** 725 * Removes the rights associated with the given rights-protected content. 726 * 727 * @param path Path to the rights-protected content. 728 * 729 * @return ERROR_NONE for success; ERROR_UNKNOWN for failure. 730 */ removeRights(String path)731 public int removeRights(String path) { 732 if (null == path || path.equals("")) { 733 throw new IllegalArgumentException("Given path should be non null"); 734 } 735 return _removeRights(mUniqueId, path); 736 } 737 738 /** 739 * Removes the rights associated with the given rights-protected content. 740 * 741 * @param uri URI for the rights-protected content. 742 * 743 * @return ERROR_NONE for success; ERROR_UNKNOWN for failure. 744 */ removeRights(Uri uri)745 public int removeRights(Uri uri) { 746 if (null == uri || Uri.EMPTY == uri) { 747 throw new IllegalArgumentException("Given uri is not valid"); 748 } 749 return removeRights(convertUriToPath(uri)); 750 } 751 752 /** 753 * Removes all the rights information of every DRM plug-in (agent) associated with 754 * the DRM framework. 755 * 756 * @return ERROR_NONE for success; ERROR_UNKNOWN for failure. 757 */ removeAllRights()758 public int removeAllRights() { 759 int result = ERROR_UNKNOWN; 760 if (null != mEventHandler) { 761 Message msg = mEventHandler.obtainMessage(ACTION_REMOVE_ALL_RIGHTS); 762 result = (mEventHandler.sendMessage(msg)) ? ERROR_NONE : result; 763 } 764 return result; 765 } 766 767 /** 768 * Initiates a new conversion session. An application must initiate a conversion session 769 * with this method each time it downloads a rights-protected file that needs to be converted. 770 *<p> 771 * This method applies only to forward-locking (copy protection) DRM schemes. 772 * 773 * @param mimeType MIME type of the input data packet. 774 * 775 * @return A convert ID that is used used to maintain the conversion session. 776 */ openConvertSession(String mimeType)777 public int openConvertSession(String mimeType) { 778 if (null == mimeType || mimeType.equals("")) { 779 throw new IllegalArgumentException("Path or the mimeType should be non null"); 780 } 781 return _openConvertSession(mUniqueId, mimeType); 782 } 783 784 /** 785 * Converts the input data (content) that is part of a rights-protected file. The converted 786 * data and status is returned in a {@link DrmConvertedStatus} object. This method should be 787 * called each time there is a new block of data received by the application. 788 * 789 * @param convertId Handle for the conversion session. 790 * @param inputData Input data that needs to be converted. 791 * 792 * @return A {@link DrmConvertedStatus} object that contains the status of the data conversion, 793 * the converted data, and offset for the header and body signature. An application can 794 * ignore the offset because it is only relevant to the 795 * {@link #closeConvertSession closeConvertSession()} method. 796 */ convertData(int convertId, byte[] inputData)797 public DrmConvertedStatus convertData(int convertId, byte[] inputData) { 798 if (null == inputData || 0 >= inputData.length) { 799 throw new IllegalArgumentException("Given inputData should be non null"); 800 } 801 return _convertData(mUniqueId, convertId, inputData); 802 } 803 804 /** 805 * Informs the DRM plug-in (agent) that there is no more data to convert or that an error 806 * has occurred. Upon successful conversion of the data, the DRM agent will provide an offset 807 * value indicating where the header and body signature should be added. Appending the 808 * signature is necessary to protect the integrity of the converted file. 809 * 810 * @param convertId Handle for the conversion session. 811 * 812 * @return A {@link DrmConvertedStatus} object that contains the status of the data conversion, 813 * the converted data, and the offset for the header and body signature. 814 */ closeConvertSession(int convertId)815 public DrmConvertedStatus closeConvertSession(int convertId) { 816 return _closeConvertSession(mUniqueId, convertId); 817 } 818 getEventType(int infoType)819 private int getEventType(int infoType) { 820 int eventType = -1; 821 822 switch (infoType) { 823 case DrmInfoRequest.TYPE_REGISTRATION_INFO: 824 case DrmInfoRequest.TYPE_UNREGISTRATION_INFO: 825 case DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO: 826 eventType = DrmEvent.TYPE_DRM_INFO_PROCESSED; 827 break; 828 } 829 return eventType; 830 } 831 getErrorType(int infoType)832 private int getErrorType(int infoType) { 833 int error = -1; 834 835 switch (infoType) { 836 case DrmInfoRequest.TYPE_REGISTRATION_INFO: 837 case DrmInfoRequest.TYPE_UNREGISTRATION_INFO: 838 case DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO: 839 error = DrmErrorEvent.TYPE_PROCESS_DRM_INFO_FAILED; 840 break; 841 } 842 return error; 843 } 844 845 /** 846 * This method expects uri in the following format 847 * content://media/<table_name>/<row_index> (or) 848 * file://sdcard/test.mp4 849 * http://test.com/test.mp4 850 * https://test.com/test.mp4 851 * 852 * Here <table_name> shall be "video" or "audio" or "images" 853 * <row_index> the index of the content in given table 854 */ convertUriToPath(Uri uri)855 private String convertUriToPath(Uri uri) { 856 String path = null; 857 if (null != uri) { 858 String scheme = uri.getScheme(); 859 if (null == scheme || scheme.equals("") || 860 scheme.equals(ContentResolver.SCHEME_FILE)) { 861 path = uri.getPath(); 862 863 } else if (scheme.equals("http") || scheme.equals("https")) { 864 path = uri.toString(); 865 866 } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) { 867 String[] projection = new String[] {MediaStore.MediaColumns.DATA}; 868 Cursor cursor = null; 869 try { 870 cursor = mContext.getContentResolver().query(uri, projection, null, 871 null, null); 872 if (null == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) { 873 throw new IllegalArgumentException("Given Uri could not be found" + 874 " in media store"); 875 } 876 int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA); 877 path = cursor.getString(pathIndex); 878 } catch (SQLiteException e) { 879 throw new IllegalArgumentException("Given Uri is not formatted in a way " + 880 "so that it can be found in media store."); 881 } finally { 882 if (null != cursor) { 883 cursor.close(); 884 } 885 } 886 } else { 887 throw new IllegalArgumentException("Given Uri scheme is not supported"); 888 } 889 } 890 return path; 891 } 892 893 // private native interfaces _initialize()894 private native int _initialize(); 895 _setListeners(int uniqueId, Object weak_this)896 private native void _setListeners(int uniqueId, Object weak_this); 897 _release(int uniqueId)898 private native void _release(int uniqueId); 899 _installDrmEngine(int uniqueId, String engineFilepath)900 private native void _installDrmEngine(int uniqueId, String engineFilepath); 901 _getConstraints(int uniqueId, String path, int usage)902 private native ContentValues _getConstraints(int uniqueId, String path, int usage); 903 _getMetadata(int uniqueId, String path)904 private native ContentValues _getMetadata(int uniqueId, String path); 905 _canHandle(int uniqueId, String path, String mimeType)906 private native boolean _canHandle(int uniqueId, String path, String mimeType); 907 _processDrmInfo(int uniqueId, DrmInfo drmInfo)908 private native DrmInfoStatus _processDrmInfo(int uniqueId, DrmInfo drmInfo); 909 _acquireDrmInfo(int uniqueId, DrmInfoRequest drmInfoRequest)910 private native DrmInfo _acquireDrmInfo(int uniqueId, DrmInfoRequest drmInfoRequest); 911 _saveRights( int uniqueId, DrmRights drmRights, String rightsPath, String contentPath)912 private native int _saveRights( 913 int uniqueId, DrmRights drmRights, String rightsPath, String contentPath); 914 _getDrmObjectType(int uniqueId, String path, String mimeType)915 private native int _getDrmObjectType(int uniqueId, String path, String mimeType); 916 _getOriginalMimeType(int uniqueId, String path, FileDescriptor fd)917 private native String _getOriginalMimeType(int uniqueId, String path, FileDescriptor fd); 918 _checkRightsStatus(int uniqueId, String path, int action)919 private native int _checkRightsStatus(int uniqueId, String path, int action); 920 _removeRights(int uniqueId, String path)921 private native int _removeRights(int uniqueId, String path); 922 _removeAllRights(int uniqueId)923 private native int _removeAllRights(int uniqueId); 924 _openConvertSession(int uniqueId, String mimeType)925 private native int _openConvertSession(int uniqueId, String mimeType); 926 _convertData( int uniqueId, int convertId, byte[] inputData)927 private native DrmConvertedStatus _convertData( 928 int uniqueId, int convertId, byte[] inputData); 929 _closeConvertSession(int uniqueId, int convertId)930 private native DrmConvertedStatus _closeConvertSession(int uniqueId, int convertId); 931 _getAllSupportInfo(int uniqueId)932 private native DrmSupportInfo[] _getAllSupportInfo(int uniqueId); 933 createEventThreads()934 private void createEventThreads() { 935 if (mEventHandler == null && mInfoHandler == null) { 936 mInfoThread = new HandlerThread("DrmManagerClient.InfoHandler"); 937 mInfoThread.start(); 938 mInfoHandler = new InfoHandler(mInfoThread.getLooper()); 939 940 mEventThread = new HandlerThread("DrmManagerClient.EventHandler"); 941 mEventThread.start(); 942 mEventHandler = new EventHandler(mEventThread.getLooper()); 943 } 944 } 945 createListeners()946 private void createListeners() { 947 _setListeners(mUniqueId, new WeakReference<DrmManagerClient>(this)); 948 } 949 } 950 951