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