Commit 5102a7c1 authored by aberent's avatar aberent Committed by Commit bot

[Android] Use BUNDLE and BUNDLE_ARRAY app restriction types

From Android M app restrictions may contain bundles and arrays of
bundles. Support this in Chrome. Convert them to JSON for transfer
to the native side.

BUG=525441

Review-Url: https://codereview.chromium.org/2543573003
Cr-Commit-Position: refs/heads/master@{#436264}
parent 3b2400e8
......@@ -44,6 +44,7 @@ junit_binary("components_policy_junit_tests") {
java_files = [
"junit/src/org/chromium/policy/AbstractAppRestrictionsProviderTest.java",
"junit/src/org/chromium/policy/CombinedPolicyProviderTest.java",
"junit/src/org/chromium/policy/PolicyConverterTest.java",
"junit/src/org/chromium/policy/test/annotations/PoliciesTest.java",
]
deps = [
......
......@@ -4,9 +4,22 @@
package org.chromium.policy;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import java.util.Arrays;
import java.util.Set;
/**
* Allows converting Java policies, contained as key/value pairs in {@link android.os.Bundle}s to
* native {@code PolicyBundle}s.
......@@ -19,6 +32,7 @@ import org.chromium.base.annotations.JNINamespace;
*/
@JNINamespace("policy::android")
public class PolicyConverter {
private static final String TAG = "PolicyConverter";
private long mNativePolicyConverter;
private PolicyConverter(long nativePolicyConverter) {
......@@ -45,11 +59,69 @@ public class PolicyConverter {
nativeSetPolicyStringArray(mNativePolicyConverter, key, (String[]) value);
return;
}
// App restrictions can only contain bundles and bundle arrays on Android M, however our
// version of Robolectric only supports Lollipop, and allowing this on LOLLIPOP doesn't
// cause problems.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (value instanceof Bundle) {
Bundle bundle = (Bundle) value;
// JNI can't take a Bundle argument without a lot of extra work, but the native code
// already accepts arbitrary JSON strings, so convert to JSON.
try {
nativeSetPolicyString(
mNativePolicyConverter, key, convertBundleToJson(bundle).toString());
} catch (JSONException e) {
// Chrome requires all policies to be expressible as JSON, so this can't be a
// valid policy.
Log.w(TAG, "Invalid bundle in app restrictions " + bundle.toString()
+ " for key " + key);
}
return;
}
if (value instanceof Bundle[]) {
Bundle[] bundleArray = (Bundle[]) value;
// JNI can't take a Bundle[] argument without a lot of extra work, but the native
// code already accepts arbitrary JSON strings, so convert to JSON.
try {
nativeSetPolicyString(mNativePolicyConverter, key,
convertBundleArrayToJson(bundleArray).toString());
} catch (JSONException e) {
// Chrome requires all policies to be expressible as JSON, so this can't be a
// valid policy.
Log.w(TAG, "Invalid bundle array in app restrictions "
+ Arrays.toString(bundleArray) + " for key " + key);
}
return;
}
}
assert false : "Invalid setting " + value + " for key " + key;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private JSONObject convertBundleToJson(Bundle bundle) throws JSONException {
JSONObject json = new JSONObject();
Set<String> keys = bundle.keySet();
for (String key : keys) {
Object value = bundle.get(key);
if (value instanceof Bundle) value = convertBundleToJson((Bundle) value);
if (value instanceof Bundle[]) value = convertBundleArrayToJson((Bundle[]) value);
json.put(key, JSONObject.wrap(value));
}
return json;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private JSONArray convertBundleArrayToJson(Bundle[] bundleArray) throws JSONException {
JSONArray json = new JSONArray();
for (Bundle bundle : bundleArray) {
json.put(convertBundleToJson(bundle));
}
return json;
}
@VisibleForTesting
@CalledByNative
private static PolicyConverter create(long nativePolicyConverter) {
static PolicyConverter create(long nativePolicyConverter) {
return new PolicyConverter(nativePolicyConverter);
}
......@@ -58,12 +130,13 @@ public class PolicyConverter {
mNativePolicyConverter = 0;
}
private native void nativeSetPolicyBoolean(
long nativePolicyConverter, String policyKey, boolean value);
private native void nativeSetPolicyInteger(
long nativePolicyConverter, String policyKey, int value);
private native void nativeSetPolicyString(
long nativePolicyConverter, String policyKey, String value);
private native void nativeSetPolicyStringArray(
@VisibleForTesting
native void nativeSetPolicyBoolean(long nativePolicyConverter, String policyKey, boolean value);
@VisibleForTesting
native void nativeSetPolicyInteger(long nativePolicyConverter, String policyKey, int value);
@VisibleForTesting
native void nativeSetPolicyString(long nativePolicyConverter, String policyKey, String value);
@VisibleForTesting
native void nativeSetPolicyStringArray(
long nativePolicyConverter, String policyKey, String[] value);
}
// Copyright 2016 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.policy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.os.Build;
import android.os.Bundle;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.chromium.testing.local.LocalRobolectricTestRunner;
/**
* Robolectric test for AbstractAppRestrictionsProvider.
*/
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = Build.VERSION_CODES.LOLLIPOP)
public class PolicyConverterTest {
/**
* Test method for
* {@link org.chromium.policy.PolicyConverter#setPolicy(java.lang.String, java.lang.Object)}.
*/
@Test
public void testSetPolicy() {
// Stub out the native methods.
PolicyConverter policyConverter = spy(PolicyConverter.create(1234));
doNothing()
.when(policyConverter)
.nativeSetPolicyBoolean(anyLong(), anyString(), anyBoolean());
doNothing().when(policyConverter).nativeSetPolicyInteger(anyLong(), anyString(), anyInt());
doNothing()
.when(policyConverter)
.nativeSetPolicyString(anyLong(), anyString(), anyString());
doNothing()
.when(policyConverter)
.nativeSetPolicyStringArray(anyLong(), anyString(), any(String[].class));
policyConverter.setPolicy("p1", true);
verify(policyConverter).nativeSetPolicyBoolean(1234, "p1", true);
policyConverter.setPolicy("p1", 5678);
verify(policyConverter).nativeSetPolicyInteger(1234, "p1", 5678);
policyConverter.setPolicy("p1", "hello");
verify(policyConverter).nativeSetPolicyString(1234, "p1", "hello");
policyConverter.setPolicy("p1", new String[] {"hello", "goodbye"});
verify(policyConverter)
.nativeSetPolicyStringArray(1234, "p1", new String[] {"hello", "goodbye"});
Bundle b1 = new Bundle();
b1.putInt("i1", 23);
b1.putString("s1", "a string");
Bundle[] ba = new Bundle[1];
ba[0] = new Bundle();
ba[0].putBoolean("ba1b", true);
ba[0].putString("ba1s", "another string");
b1.putParcelableArray("b1b", ba);
policyConverter.setPolicy("p1", b1);
verify(policyConverter)
.nativeSetPolicyString(1234, "p1", "{\"i1\":23,\"s1\":\"a string\","
+ "\"b1b\":[{\"ba1b\":true,\"ba1s\":\"another string\"}]}");
policyConverter.setPolicy("p1", ba);
verify(policyConverter)
.nativeSetPolicyString(1234, "p1", "[{\"ba1b\":true,\"ba1s\":\"another string\"}]");
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment