Bug 1297969 - Extract class RemoteManager from CodecProxy.java to RemoteManager.java for further usage. r=jolin
☠☠ backed out by 3aa804757b5b ☠ ☠
authorJames Cheng <jacheng@mozilla.com>
Thu, 25 Aug 2016 15:16:50 +0800
changeset 353007 4542cf942dab3adaf88e895fa5a360fea362d4e5
parent 353006 fa10bd34b8f7cbfbe275c6ebe9a33b583e179b3a
child 353008 031a2f4d90469a02f3ee8b28ed1605b32b0b1ec2
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjolin
bugs1297969
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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',