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