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