Skip to content
Permalink
Browse files

[WebLayer] Use AIDL in WebLayer between public and weblayer_private

This converts all communication between the public client code and
weblayer_private to use AIDL, and public no longer depends on any of the
*Impl classes.

The ObjectWrapper class used to pass the Activity over AIDL and was copied from
chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ObjectWrapper.java.

Change-Id: I9276361871888a3a4017a4e91fed4bdfff0b216e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1774979
Commit-Queue: Clark DuVall <cduvall@chromium.org>
Reviewed-by: Richard Coles <torne@chromium.org>
Reviewed-by: Bo <boliu@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#692746}
  • Loading branch information
clarkduvall authored and Commit Bot committed Sep 3, 2019
1 parent 224cb93 commit fb37334c45070470bbb3802bba14772b4fdc0e71
@@ -2665,6 +2665,9 @@ def _GetOwnersFilesToCheckForIpcOwners(input_api):
'third_party/protobuf/benchmarks/python/*',
'third_party/third_party/blink/renderer/platform/bindings/*',
'third_party/win_build_output/*',
# These aidl files are just used to communicate between class loaders
# running in the same process.
'weblayer/browser/java/org/chromium/weblayer_private/aidl/*',
]

# Dictionary mapping an OWNERS file path to Patterns.
@@ -7,14 +7,14 @@ import("//build/config/android/rules.gni")

android_library("java") {
java_files = [
"org/chromium/weblayer_private/BrowserControllerClient.java",
"org/chromium/weblayer_private/BrowserControllerImpl.java",
"org/chromium/weblayer_private/BrowserObserverProxy.java",
"org/chromium/weblayer_private/ProfileImpl.java",
"org/chromium/weblayer_private/WebLayerImpl.java",
]

deps = [
":client_java",
"//base:base_java",
"//components/embedder_support/android:content_view_java",
"//components/embedder_support/android:view_java",
@@ -30,3 +30,20 @@ generate_jni("jni") {
"org/chromium/weblayer_private/ProfileImpl.java",
]
}

android_library("client_java") {
java_files = [ "org/chromium/weblayer_private/aidl/ObjectWrapper.java" ]

srcjar_deps = [ ":aidl" ]
}

android_aidl("aidl") {
import_include = [ "org/chromium/weblayer_private/aidl" ]
sources = [
"org/chromium/weblayer_private/aidl/IBrowserController.aidl",
"org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl",
"org/chromium/weblayer_private/aidl/IObjectWrapper.aidl",
"org/chromium/weblayer_private/aidl/IProfile.aidl",
"org/chromium/weblayer_private/aidl/IWebLayer.aidl",
]
}
@@ -18,9 +18,13 @@
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.weblayer_private.aidl.IBrowserController;
import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
import org.chromium.weblayer_private.aidl.IObjectWrapper;
import org.chromium.weblayer_private.aidl.ObjectWrapper;

@JNINamespace("weblayer")
public final class BrowserControllerImpl {
public final class BrowserControllerImpl extends IBrowserController.Stub {
private long mNativeBrowserController;

private ActivityWindowAndroid mWindowAndroid;
@@ -54,8 +58,7 @@ public boolean super_onGenericMotionEvent(MotionEvent event) {
public void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix) {}
}

public BrowserControllerImpl(
Activity activity, ProfileImpl profile, BrowserControllerClient client) {
public BrowserControllerImpl(Activity activity, ProfileImpl profile) {
mProfile = profile;

mLinearLayout = new LinearLayout(activity);
@@ -79,16 +82,23 @@ public BrowserControllerImpl(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 1f));
mWebContents.onShow();
}

@Override
public void setClient(IBrowserControllerClient client) {
mBrowserObserverProxy = new BrowserObserverProxy(mNativeBrowserController, client);
}

@Override
public void destroy() {
mBrowserObserverProxy.destroy();
if (mBrowserObserverProxy != null) mBrowserObserverProxy.destroy();
nativeDeleteBrowserController(mNativeBrowserController);
mNativeBrowserController = 0;
}

public void setTopView(View view) {
@Override
public void setTopView(IObjectWrapper viewWrapper) {
View view = ObjectWrapper.unwrap(viewWrapper, View.class);
if (mTopView == view) return;
if (mTopView != null) mLinearLayout.removeView(mTopView);
mTopView = view;
@@ -100,6 +110,7 @@ public void setTopView(View view) {
}

// TODO: this is temporary, move to NavigationControllerImpl.
@Override
public void navigate(String uri) {
mWebContents.getNavigationController().loadUrl(new LoadUrlParams(uri));
}
@@ -4,15 +4,22 @@

package org.chromium.weblayer_private;

import android.os.RemoteException;
import android.util.AndroidRuntimeException;

import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.weblayer_private.aidl.IBrowserControllerClient;

@JNINamespace("weblayer")
public final class BrowserObserverProxy {
private static final String TAG = "WL_BObserverProxy";

private long mNativeBrowserObserverProxy;
private BrowserControllerClient mClient;
private IBrowserControllerClient mClient;

BrowserObserverProxy(long browserController, BrowserControllerClient client) {
BrowserObserverProxy(long browserController, IBrowserControllerClient client) {
mClient = client;
mNativeBrowserObserverProxy = nativeCreateBrowserObsererProxy(this, browserController);
}
@@ -23,7 +30,12 @@ public void destroy() {

@CalledByNative
private void displayURLChanged(String string) {
mClient.displayURLChanged(string);
try {
mClient.displayURLChanged(string);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call displayURLChanged.", e);
throw new AndroidRuntimeException(e);
}
}

private static native long nativeCreateBrowserObsererProxy(
@@ -4,25 +4,38 @@

package org.chromium.weblayer_private;

import android.app.Activity;

import org.chromium.base.annotations.JNINamespace;
import org.chromium.weblayer_private.aidl.IBrowserController;
import org.chromium.weblayer_private.aidl.IObjectWrapper;
import org.chromium.weblayer_private.aidl.IProfile;
import org.chromium.weblayer_private.aidl.ObjectWrapper;

@JNINamespace("weblayer")
public final class ProfileImpl {
public final class ProfileImpl extends IProfile.Stub {
private long mNativeProfile;

public ProfileImpl(String path) {
mNativeProfile = nativeCreateProfile(path);
}

@Override
public void destroy() {
nativeDeleteProfile(mNativeProfile);
mNativeProfile = 0;
}

@Override
public void clearBrowsingData() {
nativeClearBrowsingData(mNativeProfile);
}

@Override
public IBrowserController createBrowserController(IObjectWrapper activity) {
return new BrowserControllerImpl(ObjectWrapper.unwrap(activity, Activity.class), this);
}

long getNativeProfile() {
return mNativeProfile;
}
@@ -4,17 +4,30 @@

package org.chromium.weblayer_private;

import android.os.IBinder;

import org.chromium.base.Log;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.library_loader.ProcessInitException;
import org.chromium.content_public.browser.BrowserStartupController;
import org.chromium.content_public.browser.DeviceUtils;
import org.chromium.weblayer_private.aidl.IProfile;
import org.chromium.weblayer_private.aidl.IWebLayer;

public final class WebLayerImpl {
public final class WebLayerImpl extends IWebLayer.Stub {
// TODO: should there be one tag for all this code?
private static final String TAG = "WebLayer";

public static IBinder create() {
return new WebLayerImpl();
}

@Override
public IProfile createProfile(String path) {
return new ProfileImpl(path);
}

public WebLayerImpl() {
DeviceUtils.addDeviceSpecificUserAgentSwitch();

@@ -0,0 +1,18 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.weblayer_private.aidl;

import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
import org.chromium.weblayer_private.aidl.IObjectWrapper;

interface IBrowserController {
void setClient(in IBrowserControllerClient client) = 0;

void navigate(in String url) = 1;

void setTopView(in IObjectWrapper view) = 2;

void destroy() = 3;
}
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.weblayer_private;
package org.chromium.weblayer_private.aidl;

/**
* Interface used by BrowserController to inform the client of changes. This largely duplicates the
* BrowserObserver interface, but is a singleton to avoid unnecessary IPC.
*/
public interface BrowserControllerClient { public void displayURLChanged(String url); }
interface IBrowserControllerClient {
/** The Uri that should be displayed in the url-bar has updated. */
void displayURLChanged(in String url) = 0;
}
@@ -0,0 +1,14 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.weblayer_private.aidl;

/**
* This interface intentionally has no methods, and instances of this should
* be created from class ObjectWrapper only. This is used as a way of passing
* objects that descend from the system classes via AIDL across classloaders
* without serializing them.
*/
interface IObjectWrapper {
}
@@ -0,0 +1,16 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.weblayer_private.aidl;

import org.chromium.weblayer_private.aidl.IBrowserController;
import org.chromium.weblayer_private.aidl.IObjectWrapper;

interface IProfile {
void destroy() = 0;

void clearBrowsingData() = 1;

IBrowserController createBrowserController(in IObjectWrapper activity) = 2;
}
@@ -0,0 +1,11 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.weblayer_private.aidl;

import org.chromium.weblayer_private.aidl.IProfile;

interface IWebLayer {
IProfile createProfile(in String path) = 0;
}
@@ -0,0 +1,102 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.weblayer_private.aidl;

import android.os.IBinder;

import java.lang.reflect.Field;

/**
* This wraps an object to be transferred across sibling classloaders in the same process via the
* IObjectWrapper AIDL interface. By using reflection to retrieve the object, no serialization needs
* to occur.
*
* @param <T> The type of the wrapped object.
*/
public final class ObjectWrapper<T> extends IObjectWrapper.Stub {
/**
* The wrapped object. You must not add another member in this class because the check for
* retrieving this member variable is that this is the ONLY member variable declared in this
* class and it is private. This is because ObjectWrapper can be obfuscated, so that this member
* variable can have an obfuscated name.
*/
private final T mWrappedObject;

/* DO NOT ADD NEW CLASS MEMBERS (see above) */

/** Disable creating an object wrapper. Instead, use {@link #wrap(Object)}. */
private ObjectWrapper(T object) {
mWrappedObject = object;
}

/**
* Create the wrapped object.
*
* @param object The object instance to wrap.
* @return The wrapped object.
*/
public static <T> IObjectWrapper wrap(T object) {
return new ObjectWrapper<T>(object);
}

/**
* Unwrap the object within the {@link IObjectWrapper} using reflection.
*
* @param remote The {@link IObjectWrapper} instance to unwrap.
* @param clazz The {@link Class} of the unwrapped object type.
* @return The unwrapped object.
*/
public static <T> T unwrap(IObjectWrapper remote, Class<T> clazz) {
if (remote == null) return null;

// Handle the case when not getting an IObjectWrapper from a sibling classloader
if (remote instanceof ObjectWrapper) {
@SuppressWarnings("unchecked")
ObjectWrapper<T> typedRemote = ((ObjectWrapper<T>) remote);
return typedRemote.mWrappedObject;
}

IBinder remoteBinder = remote.asBinder();

// It is possible that ObjectWrapper was obfuscated in which case wrappedObject
// would have a different name. The following checks that there is a single
// declared field that is private.
Class<?> remoteClazz = remoteBinder.getClass();

Field validField = null;
for (Field field : remoteClazz.getDeclaredFields()) {
if (field.isSynthetic()) continue;

// Only one valid, non-synthetic field is allowed on the class.
if (validField != null) {
validField = null;
break;
}
validField = field;
}

if (validField == null || validField.isAccessible()) {
throw new IllegalArgumentException("The concrete class implementing IObjectWrapper"
+ " must have exactly *one* declared *private* field for the wrapped object. "
+ " Preferably, this is an instance of the ObjectWrapper<T> class.");
}

validField.setAccessible(true);
try {
Object wrappedObject = validField.get(remoteBinder);
if (wrappedObject == null) return null;
if (!clazz.isInstance(wrappedObject)) {
throw new IllegalArgumentException("remoteBinder is the wrong class.");
}
return clazz.cast(wrappedObject);
} catch (NullPointerException e) {
throw new IllegalArgumentException("Binder object is null.", e);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("remoteBinder is the wrong class.", e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Could not access the field in remoteBinder.", e);
}
}
}

0 comments on commit fb37334

Please sign in to comment.
You can’t perform that action at this time.