Merge "DRM framwork bug fix: add an API to release resources"
diff --git a/api/current.txt b/api/current.txt
index ddf5baf..a0405ee 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7494,6 +7494,7 @@
     method public java.lang.String getOriginalMimeType(android.net.Uri);
     method public int openConvertSession(java.lang.String);
     method public int processDrmInfo(android.drm.DrmInfo);
+    method public void release();
     method public int removeAllRights();
     method public int removeRights(java.lang.String);
     method public int removeRights(android.net.Uri);
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 9a7194c..f9567a5 100755
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -49,6 +49,8 @@
      */
     public static final int ERROR_UNKNOWN = -2000;
 
+    HandlerThread mInfoThread;
+    HandlerThread mEventThread;
     private static final String TAG = "DrmManagerClient";
 
     static {
@@ -105,6 +107,7 @@
 
     private int mUniqueId;
     private int mNativeContext;
+    private boolean mReleased;
     private Context mContext;
     private InfoHandler mInfoHandler;
     private EventHandler mEventHandler;
@@ -238,32 +241,73 @@
      */
     public DrmManagerClient(Context context) {
         mContext = context;
-
-        HandlerThread infoThread = new HandlerThread("DrmManagerClient.InfoHandler");
-        infoThread.start();
-        mInfoHandler = new InfoHandler(infoThread.getLooper());
-
-        HandlerThread eventThread = new HandlerThread("DrmManagerClient.EventHandler");
-        eventThread.start();
-        mEventHandler = new EventHandler(eventThread.getLooper());
+        mReleased = false;
 
         // save the unique id
-        mUniqueId = _initialize(new WeakReference<DrmManagerClient>(this));
+        mUniqueId = _initialize();
     }
 
     protected void finalize() {
-        _finalize(mUniqueId);
+        if (!mReleased) {
+            Log.w(TAG, "You should have called release()");
+            release();
+        }
     }
 
     /**
+     * Releases resources associated with the current session of DrmManagerClient.
+     *
+     * It is considered good practice to call this method when the {@link DrmManagerClient} object
+     * is no longer needed in your application. After release() is called,
+     * {@link DrmManagerClient} is no longer usable since it has lost all of its required resource.
+     */
+    public void release() {
+        if (mReleased) {
+            Log.w(TAG, "You have already called release()");
+            return;
+        }
+        mReleased = true;
+        if (mEventHandler != null) {
+            mEventThread.quit();
+            mEventThread = null;
+        }
+        if (mInfoHandler != null) {
+            mInfoThread.quit();
+            mInfoThread = null;
+        }
+        mEventHandler = null;
+        mInfoHandler = null;
+        mOnEventListener = null;
+        mOnInfoListener = null;
+        mOnErrorListener = null;
+        _release(mUniqueId);
+    }
+
+
+    private void createListeners() {
+        if (mEventHandler == null && mInfoHandler == null) {
+            mInfoThread = new HandlerThread("DrmManagerClient.InfoHandler");
+            mInfoThread.start();
+            mInfoHandler = new InfoHandler(mInfoThread.getLooper());
+
+            mEventThread = new HandlerThread("DrmManagerClient.EventHandler");
+            mEventThread.start();
+            mEventHandler = new EventHandler(mEventThread.getLooper());
+            _setListeners(mUniqueId, new WeakReference<DrmManagerClient>(this));
+        }
+    }
+
+
+    /**
      * Registers an {@link DrmManagerClient.OnInfoListener} callback, which is invoked when the 
      * DRM framework sends status or warning information during registration or rights acquisition.
      *
      * @param infoListener Interface definition for the callback.
      */
     public synchronized void setOnInfoListener(OnInfoListener infoListener) {
+        mOnInfoListener = infoListener;
         if (null != infoListener) {
-            mOnInfoListener = infoListener;
+            createListeners();
         }
     }
 
@@ -274,8 +318,9 @@
      * @param eventListener Interface definition for the callback.
      */
     public synchronized void setOnEventListener(OnEventListener eventListener) {
+        mOnEventListener = eventListener;
         if (null != eventListener) {
-            mOnEventListener = eventListener;
+            createListeners();
         }
     }
 
@@ -286,8 +331,9 @@
      * @param errorListener Interface definition for the callback.
      */
     public synchronized void setOnErrorListener(OnErrorListener errorListener) {
+        mOnErrorListener = errorListener;
         if (null != errorListener) {
-            mOnErrorListener = errorListener;
+            createListeners();
         }
     }
 
@@ -792,9 +838,11 @@
     }
 
     // private native interfaces
-    private native int _initialize(Object weak_this);
+    private native int _initialize();
 
-    private native void _finalize(int uniqueId);
+    private native void _setListeners(int uniqueId, Object weak_this);
+
+    private native void _release(int uniqueId);
 
     private native void _installDrmEngine(int uniqueId, String engineFilepath);
 
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index dfc7fb2..cf58177 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -225,25 +225,32 @@
 }
 
 static jint android_drm_DrmManagerClient_initialize(
-        JNIEnv* env, jobject thiz, jobject weak_thiz) {
+        JNIEnv* env, jobject thiz) {
     ALOGV("initialize - Enter");
 
     int uniqueId = 0;
     sp<DrmManagerClientImpl> drmManager = DrmManagerClientImpl::create(&uniqueId, false);
     drmManager->addClient(uniqueId);
 
-    // Set the listener to DrmManager
-    sp<DrmManagerClient::OnInfoListener> listener = new JNIOnInfoListener(env, thiz, weak_thiz);
-    drmManager->setOnInfoListener(uniqueId, listener);
-
     setDrmManagerClientImpl(env, thiz, drmManager);
     ALOGV("initialize - Exit");
-
     return uniqueId;
 }
 
-static void android_drm_DrmManagerClient_finalize(JNIEnv* env, jobject thiz, jint uniqueId) {
-    ALOGV("finalize - Enter");
+static void android_drm_DrmManagerClient_setListeners(
+        JNIEnv* env, jobject thiz, jint uniqueId, jobject weak_thiz) {
+    ALOGV("setListeners - Enter");
+
+    // Set the listener to DrmManager
+    sp<DrmManagerClient::OnInfoListener> listener = new JNIOnInfoListener(env, thiz, weak_thiz);
+    getDrmManagerClientImpl(env, thiz)->setOnInfoListener(uniqueId, listener);
+
+    ALOGV("setListeners - Exit");
+}
+
+static void android_drm_DrmManagerClient_release(
+        JNIEnv* env, jobject thiz, jint uniqueId) {
+    ALOGV("release - Enter");
     DrmManagerClientImpl::remove(uniqueId);
     getDrmManagerClientImpl(env, thiz)->setOnInfoListener(uniqueId, NULL);
 
@@ -252,7 +259,7 @@
         oldClient->setOnInfoListener(uniqueId, NULL);
         oldClient->removeClient(uniqueId);
     }
-    ALOGV("finalize - Exit");
+    ALOGV("release - Exit");
 }
 
 static jobject android_drm_DrmManagerClient_getConstraintsFromContent(
@@ -714,11 +721,14 @@
 
 static JNINativeMethod nativeMethods[] = {
 
-    {"_initialize", "(Ljava/lang/Object;)I",
+    {"_initialize", "()I",
                                     (void*)android_drm_DrmManagerClient_initialize},
 
-    {"_finalize", "(I)V",
-                                    (void*)android_drm_DrmManagerClient_finalize},
+    {"_setListeners", "(ILjava/lang/Object;)V",
+                                    (void*)android_drm_DrmManagerClient_setListeners},
+
+    {"_release", "(I)V",
+                                    (void*)android_drm_DrmManagerClient_release},
 
     {"_getConstraints", "(ILjava/lang/String;I)Landroid/content/ContentValues;",
                                     (void*)android_drm_DrmManagerClient_getConstraintsFromContent},