Bug 1306196-[P2] Provide RemoteMediaDrmBridge & corresponding stub to underlying GeckoMediaDrmBridge implementation. r=cpearce,jchen

MozReview-Commit-ID: K49zWmuJOpt

--HG--
extra : rebase_source : cc721396185cc4730a6c081f385f0d8cebbd4c7b
This commit is contained in:
Kilik Kuo 2016-11-01 23:46:34 +08:00
parent 998d263f00
commit f0150be625
5 changed files with 419 additions and 1 deletions

View File

@ -25,7 +25,7 @@ public final class MediaManager extends Service {
public IMediaDrmBridge createRemoteMediaDrmBridge(String keySystem,
String stubId)
throws RemoteException {
return null;
return new RemoteMediaDrmBridgeStub(keySystem, stubId);
}
};

View File

@ -144,6 +144,23 @@ public final class RemoteManager implements IBinder.DeathRecipient {
private void reportDecodingProcessCrash() {
Telemetry.addToHistogram(MEDIA_DECODING_PROCESS_CRASH, 1);
}
public synchronized IMediaDrmBridge createRemoteMediaDrmBridge(String keySystem,
String stubId) {
if (mRemote == null) {
if (DEBUG) Log.d(LOGTAG, "createRemoteMediaDrmBridge failed due to not initialize");
return null;
}
try {
IMediaDrmBridge remoteBridge =
mRemote.createRemoteMediaDrmBridge(keySystem, stubId);
return remoteBridge;
} catch (RemoteException e) {
Log.e(LOGTAG, "Got exception during createRemoteMediaDrmBridge().", e);
return null;
}
}
@Override
public void binderDied() {
Log.e(LOGTAG, "remote codec is dead");

View File

@ -0,0 +1,152 @@
/* 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 android.media.MediaCrypto;
import android.util.Log;
final class RemoteMediaDrmBridge implements GeckoMediaDrm {
private static final String LOGTAG = "GeckoRemoteMediaDrmBridge";
private static final boolean DEBUG = false;
private CallbacksForwarder mCallbacksFwd;
private IMediaDrmBridge mRemote;
// Forward callbacks from remote bridge stub to MediaDrmProxy.
private static class CallbacksForwarder extends IMediaDrmBridgeCallbacks.Stub {
private final GeckoMediaDrm.Callbacks mProxyCallbacks;
CallbacksForwarder(Callbacks callbacks) {
assertTrue(callbacks != null);
mProxyCallbacks = callbacks;
}
@Override
public void onSessionCreated(int createSessionToken,
int promiseId,
byte[] sessionId,
byte[] request) {
mProxyCallbacks.onSessionCreated(createSessionToken,
promiseId,
sessionId,
request);
}
@Override
public void onSessionUpdated(int promiseId, byte[] sessionId) {
mProxyCallbacks.onSessionUpdated(promiseId, sessionId);
}
@Override
public void onSessionClosed(int promiseId, byte[] sessionId) {
mProxyCallbacks.onSessionClosed(promiseId, sessionId);
}
@Override
public void onSessionMessage(byte[] sessionId,
int sessionMessageType,
byte[] request) {
mProxyCallbacks.onSessionMessage(sessionId, sessionMessageType, request);
}
@Override
public void onSessionError(byte[] sessionId, String message) {
mProxyCallbacks.onSessionError(sessionId, message);
}
@Override
public void onSessionBatchedKeyChanged(byte[] sessionId,
SessionKeyInfo[] keyInfos) {
mProxyCallbacks.onSessionBatchedKeyChanged(sessionId, keyInfos);
}
@Override
public void onRejectPromise(int promiseId, String message) {
mProxyCallbacks.onRejectPromise(promiseId, message);
}
} // CallbacksForwarder
/* package-private */ static void assertTrue(boolean condition) {
if (DEBUG && !condition) {
throw new AssertionError("Expected condition to be true");
}
}
public RemoteMediaDrmBridge(IMediaDrmBridge remoteBridge) {
assertTrue(remoteBridge != null);
mRemote = remoteBridge;
}
@Override
public synchronized void setCallbacks(Callbacks callbacks) {
if (DEBUG) Log.d(LOGTAG, "setCallbacks()");
assertTrue(callbacks != null);
assertTrue(mRemote != null);
mCallbacksFwd = new CallbacksForwarder(callbacks);
try {
mRemote.setCallbacks(mCallbacksFwd);
} catch (Exception e) {
Log.e(LOGTAG, "Got exception during setCallbacks", e);
}
}
@Override
public synchronized void createSession(int createSessionToken,
int promiseId,
String initDataType,
byte[] initData) {
if (DEBUG) Log.d(LOGTAG, "createSession()");
try {
mRemote.createSession(createSessionToken, promiseId, initDataType, initData);
} catch (Exception e) {
Log.e(LOGTAG, "Got exception while creating remote session.", e);
mCallbacksFwd.onRejectPromise(promiseId, "Failed to create session.");
}
}
@Override
public synchronized void updateSession(int promiseId, String sessionId, byte[] response) {
if (DEBUG) Log.d(LOGTAG, "updateSession()");
try {
mRemote.updateSession(promiseId, sessionId, response);
} catch (Exception e) {
Log.e(LOGTAG, "Got exception while updating remote session.", e);
mCallbacksFwd.onRejectPromise(promiseId, "Failed to update session.");
}
}
@Override
public synchronized void closeSession(int promiseId, String sessionId) {
if (DEBUG) Log.d(LOGTAG, "closeSession()");
try {
mRemote.closeSession(promiseId, sessionId);
} catch (Exception e) {
Log.e(LOGTAG, "Got exception while closing remote session.", e);
mCallbacksFwd.onRejectPromise(promiseId, "Failed to close session.");
}
}
@Override
public synchronized void release() {
if (DEBUG) Log.d(LOGTAG, "release()");
try {
mRemote.release();
} catch (Exception e) {
Log.e(LOGTAG, "Got exception while releasing RemoteDrmBridge.", e);
}
mRemote = null;
mCallbacksFwd = null;
}
@Override
public synchronized MediaCrypto getMediaCrypto() {
if (DEBUG) Log.d(LOGTAG, "getMediaCrypto(), should not enter here!");
assertTrue(false);
return null;
}
}

View File

@ -0,0 +1,247 @@
/* 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.AppConstants;
import java.util.ArrayList;
import android.media.MediaCrypto;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
final class RemoteMediaDrmBridgeStub extends IMediaDrmBridge.Stub implements IBinder.DeathRecipient {
private static final String LOGTAG = "GeckoRemoteMediaDrmBridgeStub";
private static final boolean DEBUG = false;
private volatile IMediaDrmBridgeCallbacks mCallbacks = null;
// Underlying bridge implmenetaion, i.e. GeckoMediaDrmBrdigeV21.
private GeckoMediaDrm mBridge = null;
// mStubId is initialized during stub construction. It should be a unique
// string which is generated in MediaDrmProxy in Fennec App process and is
// used for Codec to obtain corresponding MediaCrypto as input to achieve
// decryption.
// The generated stubId will be delivered to Codec via a code path starting
// from MediaDrmProxy -> MediaDrmCDMProxy -> RemoteDataDecoder => IPC => Codec.
private String mStubId = "";
public static ArrayList<RemoteMediaDrmBridgeStub> mBridgeStubs =
new ArrayList<RemoteMediaDrmBridgeStub>();
private String getId() {
return mStubId;
}
private MediaCrypto getMediaCryptoFromBridge() {
return mBridge != null ? mBridge.getMediaCrypto() : null;
}
public static synchronized MediaCrypto getMediaCrypto(String stubId) {
if (DEBUG) Log.d(LOGTAG, "getMediaCrypto()");
for (int i = 0; i < mBridgeStubs.size(); i++) {
if (mBridgeStubs.get(i) != null &&
mBridgeStubs.get(i).getId().equals(stubId)) {
return mBridgeStubs.get(i).getMediaCryptoFromBridge();
}
}
return null;
}
// Callback to RemoteMediaDrmBridge.
private final class Callbacks implements GeckoMediaDrm.Callbacks {
private IMediaDrmBridgeCallbacks mRemoteCallbacks;
public Callbacks(IMediaDrmBridgeCallbacks remote) {
mRemoteCallbacks = remote;
}
@Override
public void onSessionCreated(int createSessionToken,
int promiseId,
byte[] sessionId,
byte[] request) {
if (DEBUG) Log.d(LOGTAG, "onSessionCreated()");
try {
mRemoteCallbacks.onSessionCreated(createSessionToken,
promiseId,
sessionId,
request);
} catch (RemoteException e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
@Override
public void onSessionUpdated(int promiseId, byte[] sessionId) {
if (DEBUG) Log.d(LOGTAG, "onSessionUpdated()");
try {
mRemoteCallbacks.onSessionUpdated(promiseId, sessionId);
} catch (RemoteException e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
@Override
public void onSessionClosed(int promiseId, byte[] sessionId) {
if (DEBUG) Log.d(LOGTAG, "onSessionClosed()");
try {
mRemoteCallbacks.onSessionClosed(promiseId, sessionId);
} catch (RemoteException e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
@Override
public void onSessionMessage(byte[] sessionId,
int sessionMessageType,
byte[] request) {
if (DEBUG) Log.d(LOGTAG, "onSessionMessage()");
try {
mRemoteCallbacks.onSessionMessage(sessionId, sessionMessageType, request);
} catch (RemoteException e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
@Override
public void onSessionError(byte[] sessionId, String message) {
if (DEBUG) Log.d(LOGTAG, "onSessionError()");
try {
mRemoteCallbacks.onSessionError(sessionId, message);
} catch (RemoteException e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
@Override
public void onSessionBatchedKeyChanged(byte[] sessionId,
SessionKeyInfo[] keyInfos) {
if (DEBUG) Log.d(LOGTAG, "onSessionBatchedKeyChanged()");
try {
mRemoteCallbacks.onSessionBatchedKeyChanged(sessionId, keyInfos);
} catch (RemoteException e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
@Override
public void onRejectPromise(int promiseId, String message) {
if (DEBUG) Log.d(LOGTAG, "onRejectPromise()");
try {
mRemoteCallbacks.onRejectPromise(promiseId, message);
} catch (RemoteException e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
}
/* package-private */ void assertTrue(boolean condition) {
if (DEBUG && !condition) {
throw new AssertionError("Expected condition to be true");
}
}
RemoteMediaDrmBridgeStub(String keySystem, String stubId) throws RemoteException {
if (AppConstants.Versions.preLollipop) {
Log.e(LOGTAG, "Pre-Lollipop should never enter here!!");
throw new RemoteException("Error, unsupported version!");
}
try {
if (AppConstants.Versions.feature21Plus &&
AppConstants.Versions.preMarshmallow) {
mBridge = new GeckoMediaDrmBridgeV21(keySystem);
} else {
mBridge = new GeckoMediaDrmBridgeV23(keySystem);
}
mStubId = stubId;
mBridgeStubs.add(this);
} catch (Exception e) {
throw new RemoteException("RemoteMediaDrmBridgeStub cannot create bridge implementation.");
}
}
@Override
public synchronized void setCallbacks(IMediaDrmBridgeCallbacks callbacks) throws RemoteException {
if (DEBUG) Log.d(LOGTAG, "setCallbacks()");
assertTrue(mBridge != null);
assertTrue(callbacks != null);
mCallbacks = callbacks;
callbacks.asBinder().linkToDeath(this, 0);
mBridge.setCallbacks(new Callbacks(mCallbacks));
}
@Override
public synchronized void createSession(int createSessionToken,
int promiseId,
String initDataType,
byte[] initData) throws RemoteException {
if (DEBUG) Log.d(LOGTAG, "createSession()");
try {
assertTrue(mCallbacks != null);
assertTrue(mBridge != null);
mBridge.createSession(createSessionToken,
promiseId,
initDataType,
initData);
} catch (Exception e) {
Log.e(LOGTAG, "Failed to createSession.", e);
mCallbacks.onRejectPromise(promiseId, "Failed to createSession.");
}
}
@Override
public synchronized void updateSession(int promiseId,
String sessionId,
byte[] response) throws RemoteException {
if (DEBUG) Log.d(LOGTAG, "updateSession()");
try {
assertTrue(mCallbacks != null);
assertTrue(mBridge != null);
mBridge.updateSession(promiseId, sessionId, response);
} catch (Exception e) {
Log.e(LOGTAG, "Failed to updateSession.", e);
mCallbacks.onRejectPromise(promiseId, "Failed to updateSession.");
}
}
@Override
public synchronized void closeSession(int promiseId, String sessionId) throws RemoteException {
if (DEBUG) Log.d(LOGTAG, "closeSession()");
try {
assertTrue(mCallbacks != null);
assertTrue(mBridge != null);
mBridge.closeSession(promiseId, sessionId);
} catch (Exception e) {
Log.e(LOGTAG, "Failed to closeSession.", e);
mCallbacks.onRejectPromise(promiseId, "Failed to closeSession.");
}
}
// IBinder.DeathRecipient
@Override
public synchronized void binderDied() {
Log.e(LOGTAG, "Binder died !!");
try {
release();
} catch (Exception e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
@Override
public synchronized void release() {
if (DEBUG) Log.d(LOGTAG, "release()");
mBridgeStubs.remove(this);
if (mBridge != null) {
mBridge.release();
mBridge = null;
}
mCallbacks.asBinder().unlinkToDeath(this, 0);
mCallbacks = null;
mStubId = "";
}
}

View File

@ -564,6 +564,8 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'media/MediaDrmProxy.java',
'media/MediaManager.java',
'media/RemoteManager.java',
'media/RemoteMediaDrmBridge.java',
'media/RemoteMediaDrmBridgeStub.java',
'media/Sample.java',
'media/SamplePool.java',
'media/SessionKeyInfo.java',