Bug 1297969 - Extract class RemoteManager from CodecProxy.java to RemoteManager.java for further usage. r?jolin draft
authorJames Cheng <jacheng@mozilla.com>
Thu, 25 Aug 2016 15:16:50 +0800
changeset 405956 66b6ab92a6d8b1ca9d98c9bc310e6d7e4a24630a
parent 404988 01748a2b1a463f24efd9cd8abad9ccfd76b037b8
child 529552 709c080628797993e2864ab2a98395bed0c08e91
push id27613
push userbmo:jacheng@mozilla.com
push dateFri, 26 Aug 2016 07:16:58 +0000
reviewersjolin
bugs1297969
milestone51.0a1
Bug 1297969 - Extract class RemoteManager from CodecProxy.java to RemoteManager.java for further usage. r?jolin MozReview-Commit-ID: Hqm6ZEzv3v1
mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
mobile/android/base/moz.build
--- a/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
@@ -11,34 +11,26 @@ import org.mozilla.gecko.mozglue.JNIObje
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.media.MediaCodec;
 import android.media.MediaCodec.BufferInfo;
 import android.media.MediaFormat;
 import android.os.DeadObjectException;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.Surface;
 
 import java.nio.ByteBuffer;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.LinkedList;
-import java.util.List;
-
 // Proxy class of ICodec binder.
 public final class CodecProxy {
     private static final String LOGTAG = "GeckoRemoteCodecProxy";
     private static final boolean DEBUG = false;
 
-    private static final RemoteManager sRemoteManager = new RemoteManager();
-
     private ICodec mRemote;
     private FormatParam mFormat;
     private Surface mOutputSurface;
     private CallbacksForwarder mCallbacks;
 
     public interface Callbacks {
         void onInputExhausted();
         void onOutputFormatChanged(MediaFormat format);
@@ -92,200 +84,46 @@ public final class CodecProxy {
             reportError(fatal);
         }
 
         public void reportError(boolean fatal) {
             mCallbacks.onError(fatal);
         }
     }
 
-    private static final class RemoteManager implements IBinder.DeathRecipient {
-        private List<CodecProxy> mProxies = new LinkedList<CodecProxy>();
-        private ICodecManager mRemote;
-        private volatile CountDownLatch mConnectionLatch;
-        private final ServiceConnection mConnection = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                if (DEBUG) Log.d(LOGTAG, "service connected");
-                try {
-                    service.linkToDeath(RemoteManager.this, 0);
-                } catch (RemoteException e) {
-                    e.printStackTrace();
-                }
-                mRemote = ICodecManager.Stub.asInterface(service);
-                if (mConnectionLatch != null) {
-                    mConnectionLatch.countDown();
-                }
-            }
-
-            /**
-             * Called when a connection to the Service has been lost.  This typically
-             * happens when the process hosting the service has crashed or been killed.
-             * This does <em>not</em> remove the ServiceConnection itself -- this
-             * binding to the service will remain active, and you will receive a call
-             * to {@link #onServiceConnected} when the Service is next running.
-             *
-             * @param name The concrete component name of the service whose
-             *             connection has been lost.
-             */
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                if (DEBUG) Log.d(LOGTAG, "service disconnected");
-                mRemote.asBinder().unlinkToDeath(RemoteManager.this, 0);
-                mRemote = null;
-                if (mConnectionLatch != null) {
-                    mConnectionLatch.countDown();
-                }
-            }
-        };
-
-        public synchronized boolean init() {
-            if (mRemote != null) {
-                return true;
-            }
-
-            if (DEBUG) Log.d(LOGTAG, "init remote manager " + this);
-            Context appCtxt = GeckoAppShell.getApplicationContext();
-            if (DEBUG) Log.d(LOGTAG, "ctxt=" + appCtxt);
-            appCtxt.bindService(new Intent(appCtxt, CodecManager.class),
-                    mConnection, Context.BIND_AUTO_CREATE);
-            if (!waitConnection()) {
-                appCtxt.unbindService(mConnection);
-                return false;
-            }
-            return true;
-        }
-
-        private boolean waitConnection() {
-            boolean ok = false;
-
-            mConnectionLatch = new CountDownLatch(1);
-            try {
-                int retryCount = 0;
-                while (retryCount < 5) {
-                    if (DEBUG) Log.d(LOGTAG, "waiting for connection latch:" + mConnectionLatch);
-                    mConnectionLatch.await(1, TimeUnit.SECONDS);
-                    if (mConnectionLatch.getCount() == 0) {
-                        break;
-                    }
-                    Log.w(LOGTAG, "Creator not connected in 1s. Try again.");
-                    retryCount++;
-                }
-                ok = true;
-            } catch (InterruptedException e) {
-                Log.e(LOGTAG, "service not connected in 5 seconds. Stop waiting.");
-                e.printStackTrace();
-            }
-            mConnectionLatch = null;
-
-            return ok;
-        }
-
-        public synchronized CodecProxy createCodec(MediaFormat format, Surface surface, Callbacks callbacks) {
-            try {
-                ICodec remote = mRemote.createCodec();
-                CodecProxy proxy = new CodecProxy(format, surface, callbacks);
-                if (proxy.init(remote)) {
-                    mProxies.add(proxy);
-                    return proxy;
-                } else {
-                    return null;
-                }
-            } catch (RemoteException e) {
-                e.printStackTrace();
-                return null;
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            Log.e(LOGTAG, "remote codec is dead");
-            handleRemoteDeath();
-        }
-
-        private synchronized void handleRemoteDeath() {
-            // Wait for onServiceDisconnected()
-            if (!waitConnection()) {
-                notifyError(true);
-                return;
-            }
-            // Restart
-            if (init() && recoverRemoteCodec()) {
-                notifyError(false);
-            } else {
-                notifyError(true);
-            }
-        }
-
-        private synchronized void notifyError(boolean fatal) {
-            for (CodecProxy proxy : mProxies) {
-                proxy.mCallbacks.reportError(fatal);
-            }
-        }
-
-        private synchronized boolean recoverRemoteCodec() {
-            if (DEBUG) Log.d(LOGTAG, "recover codec");
-            boolean ok = true;
-            try {
-                for (CodecProxy proxy : mProxies) {
-                    ok &= proxy.init(mRemote.createCodec());
-                }
-                return ok;
-            } catch (RemoteException e) {
-                return false;
-            }
-        }
-
-        private void releaseCodec(CodecProxy proxy) throws DeadObjectException, RemoteException {
-            proxy.deinit();
-            synchronized (this) {
-                if (mProxies.remove(proxy) && mProxies.isEmpty()) {
-                    release();
-                }
-            }
-        }
-
-        private void release() {
-            if (DEBUG) Log.d(LOGTAG, "release remote manager " + this);
-            Context appCtxt = GeckoAppShell.getApplicationContext();
-            mRemote.asBinder().unlinkToDeath(this, 0);
-            mRemote = null;
-            appCtxt.unbindService(mConnection);
-        }
+    @WrapForJNI
+    public static CodecProxy create(MediaFormat format, Surface surface, Callbacks callbacks) {
+        return RemoteManager.getInstance().createCodec(format, surface, callbacks);
     }
 
-    @WrapForJNI
-    public static CodecProxy create(MediaFormat format, Surface surface, Callbacks callbacks) {
-        if (!sRemoteManager.init()) {
-            return null;
-        }
-        return sRemoteManager.createCodec(format, surface, callbacks);
+    public static CodecProxy createCodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
+        return new CodecProxy(format, surface, callbacks);
     }
 
     private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
         mFormat = new FormatParam(format);
         mOutputSurface = surface;
         mCallbacks = new CallbacksForwarder(callbacks);
     }
 
-    private boolean init(ICodec remote) {
+    boolean init(ICodec remote) {
         try {
             remote.setCallbacks(mCallbacks);
             remote.configure(mFormat, mOutputSurface, 0);
             remote.start();
         } catch (RemoteException e) {
             e.printStackTrace();
             return false;
         }
 
         mRemote = remote;
         return true;
     }
 
-    private boolean deinit() {
+    boolean deinit() {
         try {
             mRemote.stop();
             mRemote.release();
             mRemote = null;
             return true;
         } catch (RemoteException e) {
             e.printStackTrace();
             return false;
@@ -333,18 +171,22 @@ public final class CodecProxy {
     @WrapForJNI
     public synchronized boolean release() {
         if (mRemote == null) {
             Log.w(LOGTAG, "codec already ended");
             return true;
         }
         if (DEBUG) Log.d(LOGTAG, "release " + this);
         try {
-            sRemoteManager.releaseCodec(this);
+            RemoteManager.getInstance().releaseCodec(this);
         } catch (DeadObjectException e) {
             return false;
         } catch (RemoteException e) {
             e.printStackTrace();
             return false;
         }
         return true;
     }
+
+    public synchronized void reportError(boolean fatal) {
+        mCallbacks.reportError(fatal);
+    }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
@@ -0,0 +1,201 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.media;
+
+import org.mozilla.gecko.GeckoAppShell;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.media.MediaFormat;
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.Surface;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.LinkedList;
+import java.util.List;
+
+public final class RemoteManager implements IBinder.DeathRecipient {
+    private static final String LOGTAG = "GeckoRemoteManager";
+    private static final boolean DEBUG = false;
+    private static RemoteManager sRemoteManager = null;
+
+    public synchronized static RemoteManager getInstance() {
+        if (sRemoteManager == null){
+            sRemoteManager = new RemoteManager();
+        }
+
+        sRemoteManager.init();
+        return sRemoteManager;
+    }
+
+    private List<CodecProxy> mProxies = new LinkedList<CodecProxy>();
+    private volatile ICodecManager mRemote;
+    private volatile CountDownLatch mConnectionLatch;
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG) Log.d(LOGTAG, "service connected");
+            try {
+                service.linkToDeath(RemoteManager.this, 0);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+            mRemote = ICodecManager.Stub.asInterface(service);
+            if (mConnectionLatch != null) {
+                mConnectionLatch.countDown();
+            }
+        }
+
+        /**
+         * Called when a connection to the Service has been lost.  This typically
+         * happens when the process hosting the service has crashed or been killed.
+         * This does <em>not</em> remove the ServiceConnection itself -- this
+         * binding to the service will remain active, and you will receive a call
+         * to {@link #onServiceConnected} when the Service is next running.
+         *
+         * @param name The concrete component name of the service whose
+         *             connection has been lost.
+         */
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) Log.d(LOGTAG, "service disconnected");
+            mRemote.asBinder().unlinkToDeath(RemoteManager.this, 0);
+            mRemote = null;
+            if (mConnectionLatch != null) {
+                mConnectionLatch.countDown();
+            }
+        }
+    };
+
+    private synchronized boolean init() {
+        if (mRemote != null) {
+            return true;
+        }
+
+        if (DEBUG) Log.d(LOGTAG, "init remote manager " + this);
+        Context appCtxt = GeckoAppShell.getApplicationContext();
+        if (DEBUG) Log.d(LOGTAG, "ctxt=" + appCtxt);
+        appCtxt.bindService(new Intent(appCtxt, CodecManager.class),
+                mConnection, Context.BIND_AUTO_CREATE);
+        if (!waitConnection()) {
+            appCtxt.unbindService(mConnection);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean waitConnection() {
+        boolean ok = false;
+
+        mConnectionLatch = new CountDownLatch(1);
+        try {
+            int retryCount = 0;
+            while (retryCount < 5) {
+                if (DEBUG) Log.d(LOGTAG, "waiting for connection latch:" + mConnectionLatch);
+                mConnectionLatch.await(1, TimeUnit.SECONDS);
+                if (mConnectionLatch.getCount() == 0) {
+                    break;
+                }
+                Log.w(LOGTAG, "Creator not connected in 1s. Try again.");
+                retryCount++;
+            }
+            ok = true;
+        } catch (InterruptedException e) {
+            Log.e(LOGTAG, "service not connected in 5 seconds. Stop waiting.");
+            e.printStackTrace();
+        }
+        mConnectionLatch = null;
+
+        return ok;
+    }
+
+    public synchronized CodecProxy createCodec(MediaFormat format,
+                                               Surface surface,
+                                               CodecProxy.Callbacks callbacks) {
+        if (mRemote == null) {
+            if (DEBUG) Log.d(LOGTAG, "createCodec failed due to not initialize");
+            return null;
+        }
+        try {
+            ICodec remote = mRemote.createCodec();
+            CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks);
+            if (proxy.init(remote)) {
+                mProxies.add(proxy);
+                return proxy;
+            } else {
+                return null;
+            }
+        } catch (RemoteException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    @Override
+    public void binderDied() {
+        Log.e(LOGTAG, "remote codec is dead");
+        handleRemoteDeath();
+    }
+
+    private synchronized void handleRemoteDeath() {
+        // Wait for onServiceDisconnected()
+        if (!waitConnection()) {
+            notifyError(true);
+            return;
+        }
+        // Restart
+        if (init() && recoverRemoteCodec()) {
+            notifyError(false);
+        } else {
+            notifyError(true);
+        }
+    }
+
+    private synchronized void notifyError(boolean fatal) {
+        for (CodecProxy proxy : mProxies) {
+            proxy.reportError(fatal);
+        }
+    }
+
+    private synchronized boolean recoverRemoteCodec() {
+        if (DEBUG) Log.d(LOGTAG, "recover codec");
+        boolean ok = true;
+        try {
+            for (CodecProxy proxy : mProxies) {
+                ok &= proxy.init(mRemote.createCodec());
+            }
+            return ok;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    public void releaseCodec(CodecProxy proxy) throws DeadObjectException, RemoteException {
+        if (mRemote == null) {
+            if (DEBUG) Log.d(LOGTAG, "releaseCodec called but not initialized yet");
+            return;
+        }
+        proxy.deinit();
+        synchronized (this) {
+            if (mProxies.remove(proxy) && mProxies.isEmpty()) {
+                release();
+            }
+        }
+    }
+
+    private void release() {
+        if (DEBUG) Log.d(LOGTAG, "release remote manager " + this);
+        Context appCtxt = GeckoAppShell.getApplicationContext();
+        mRemote.asBinder().unlinkToDeath(this, 0);
+        mRemote = null;
+        appCtxt.unbindService(mConnection);
+    }
+} // RemoteManager
\ No newline at end of file
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -503,16 +503,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'media/AsyncCodecFactory.java',
     'media/AudioFocusAgent.java',
     'media/Codec.java',
     'media/CodecManager.java',
     'media/CodecProxy.java',
     'media/FormatParam.java',
     'media/JellyBeanAsyncCodec.java',
     'media/MediaControlService.java',
+    'media/RemoteManager.java',
     'media/Sample.java',
     'MediaCastingBar.java',
     'MemoryMonitor.java',
     'menu/GeckoMenu.java',
     'menu/GeckoMenuInflater.java',
     'menu/GeckoMenuItem.java',
     'menu/GeckoSubMenu.java',
     'menu/MenuItemActionBar.java',