Bug 1042715 - Add support for Restricted Profiles r=rnewman a=lmandel
☠☠ backed out by fcf16a67fed4 ☠ ☠
authorMark Finkle <mfinkle@mozilla.com>
Wed, 20 Aug 2014 09:10:06 -0400
changeset 216682 080344c7c80b
parent 216681 d820ef3b256d
child 216683 fcf16a67fed4
push id3873
push usermfinkle@mozilla.com
push date2014-09-08 20:44 +0000
treeherdermozilla-beta@080344c7c80b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman, lmandel
bugs1042715
milestone33.0
Bug 1042715 - Add support for Restricted Profiles r=rnewman a=lmandel
mobile/android/base/GeckoAppShell.java
mobile/android/base/tests/robocop.ini
mobile/android/base/tests/testRestrictedProfiles.java
mobile/android/base/tests/testRestrictedProfiles.js
toolkit/components/parentalcontrols/moz.build
toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -22,21 +22,25 @@ import java.net.URLConnection;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Queue;
+import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
+import org.json.JSONException;
+import org.json.JSONObject;
+
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PanZoomController;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.mozglue.JNITarget;
 import org.mozilla.gecko.mozglue.RobocopTarget;
@@ -83,22 +87,24 @@ import android.location.Criteria;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
 import android.media.MediaScannerConnection;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
 import android.os.SystemClock;
+import android.os.UserManager;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -2556,16 +2562,49 @@ public class GeckoAppShell
                 return "PROXY " + proxy.address().toString();
             case SOCKS:
                 return "SOCKS " + proxy.address().toString();
         }
 
         return "DIRECT";
     }
 
+    @WrapElementForJNI
+    public static boolean isUserRestricted() {
+        if (Build.VERSION.SDK_INT < 18) {
+            return false;
+        }
+
+        UserManager mgr = (UserManager)getContext().getSystemService(Context.USER_SERVICE);
+        Bundle restrictions = mgr.getUserRestrictions();
+
+        return !restrictions.isEmpty();
+    }
+
+    @WrapElementForJNI
+    public static String getUserRestrictions() {
+        if (Build.VERSION.SDK_INT < 18) {
+            return "{}";
+        }
+
+        JSONObject json = new JSONObject();
+        UserManager mgr = (UserManager)getContext().getSystemService(Context.USER_SERVICE);
+        Bundle restrictions = mgr.getUserRestrictions();
+
+        Set<String> keys = restrictions.keySet();
+        for (String key : keys) {
+            try {
+                json.put(key, restrictions.get(key));
+            } catch (JSONException e) {
+            }
+        }
+
+        return json.toString();
+    }
+
     /* Downloads the uri pointed to by a share intent, and alters the intent to point to the locally stored file.
      */
     public static void downloadImageForIntent(final Intent intent) {
         final String src = intent.getStringExtra(Intent.EXTRA_TEXT);
         final File dir = GeckoApp.getTempDirectory();
 
         if (dir == null) {
             showImageShareFailureToast();
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -100,16 +100,17 @@ skip-if = android_version == "10"
 [testAccounts]
 [testAndroidLog]
 [testBrowserDiscovery]
 [testDebuggerServer]
 [testDeviceSearchEngine]
 [testJNI]
 # [testMozPay] # see bug 945675
 [testOrderedBroadcast]
+[testRestrictedProfiles]
 [testSharedPreferences]
 [testSimpleDiscovery]
 [testUITelemetry]
 [testVideoDiscovery]
 
 # Used for Talos, please don't use in mochitest
 #[testPan]
 #[testCheck]
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testRestrictedProfiles.java
@@ -0,0 +1,9 @@
+package org.mozilla.gecko.tests;
+
+
+
+public class testRestrictedProfiles extends JavascriptTest {
+    public testRestrictedProfiles() {
+        super("testRestrictedProfiles.js");
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testRestrictedProfiles.js
@@ -0,0 +1,45 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+/* 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/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://gre/modules/JNI.jsm");
+
+add_task(function test_isUserRestricted() {
+  // Make sure the parental controls service is available
+  do_check_true("@mozilla.org/parental-controls-service;1" in Cc);
+  
+  let pc = Cc["@mozilla.org/parental-controls-service;1"].createInstance(Ci.nsIParentalControlsService);
+  
+  // In an admin profile, like the tests: enabled = false
+  // In a restricted profile: enabled = true
+  do_check_false(pc.parentalControlsEnabled);
+
+  //run_next_test();
+});
+/*
+// NOTE: JNI.jsm has no way to call a string method yet
+add_task(function test_getUserRestrictions() {
+  // In an admin profile, like the tests: {}
+  // In a restricted profile: {"no_modify_accounts":true,"no_share_location":true}
+  let restrictions = "{}";
+
+  let jni = null;
+  try {
+    jni = new JNI();
+    let cls = jni.findClass("org/mozilla/gecko/GeckoAppShell");
+    let method = jni.getStaticMethodID(cls, "getUserRestrictions", "()Ljava/lang/String;");
+    restrictions = jni.callStaticStringMethod(cls, method);
+  } finally {
+    if (jni != null) {
+      jni.close();
+    }
+  }
+
+  do_check_eq(restrictions, "{}");
+});
+*/
+run_next_test();
--- a/toolkit/components/parentalcontrols/moz.build
+++ b/toolkit/components/parentalcontrols/moz.build
@@ -14,14 +14,18 @@ if not CONFIG['MOZ_DISABLE_PARENTAL_CONT
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
         SOURCES += [
             'nsParentalControlsServiceWin.cpp',
         ]
     elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
         UNIFIED_SOURCES += [
             'nsParentalControlsServiceCocoa.mm',
         ]
+    elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+        UNIFIED_SOURCES += [
+            'nsParentalControlsServiceAndroid.cpp',
+        ]
     else:
         SOURCES += [
             'nsParentalControlsServiceDefault.cpp',
         ]
 
 FINAL_LIBRARY = 'toolkitcomps'
new file mode 100644
--- /dev/null
+++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsParentalControlsService.h"
+#include "AndroidBridge.h"
+#include "nsString.h"
+#include "nsIFile.h"
+
+NS_IMPL_ISUPPORTS(nsParentalControlsService, nsIParentalControlsService)
+
+nsParentalControlsService::nsParentalControlsService() :
+  mEnabled(false)
+{
+  if (mozilla::AndroidBridge::HasEnv()) {
+    mEnabled = mozilla::widget::android::GeckoAppShell::IsUserRestricted();
+  }
+}
+
+nsParentalControlsService::~nsParentalControlsService()
+{
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::GetParentalControlsEnabled(bool *aResult)
+{
+  *aResult = mEnabled;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::GetBlockFileDownloadsEnabled(bool *aResult)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::GetLoggingEnabled(bool *aResult)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::Log(int16_t aEntryType,
+                               bool aBlocked,
+                               nsIURI *aSource,
+                               nsIFile *aTarget)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::RequestURIOverride(nsIURI *aTarget,
+                                              nsIInterfaceRequestor *aWindowContext,
+                                              bool *_retval)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::RequestURIOverrides(nsIArray *aTargets,
+                                               nsIInterfaceRequestor *aWindowContext,
+                                               bool *_retval)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -49,23 +49,25 @@ jmethodID GeckoAppShell::jGetIconForExte
 jmethodID GeckoAppShell::jGetMessageWrapper = 0;
 jmethodID GeckoAppShell::jGetMimeTypeFromExtensionsWrapper = 0;
 jmethodID GeckoAppShell::jGetNextMessageInListWrapper = 0;
 jmethodID GeckoAppShell::jGetProxyForURIWrapper = 0;
 jmethodID GeckoAppShell::jGetScreenDepthWrapper = 0;
 jmethodID GeckoAppShell::jGetScreenOrientationWrapper = 0;
 jmethodID GeckoAppShell::jGetShowPasswordSetting = 0;
 jmethodID GeckoAppShell::jGetSystemColoursWrapper = 0;
+jmethodID GeckoAppShell::jGetUserRestrictions = 0;
 jmethodID GeckoAppShell::jHandleGeckoMessageWrapper = 0;
 jmethodID GeckoAppShell::jHandleUncaughtException = 0;
 jmethodID GeckoAppShell::jHideProgressDialog = 0;
 jmethodID GeckoAppShell::jInitCameraWrapper = 0;
 jmethodID GeckoAppShell::jIsNetworkLinkKnown = 0;
 jmethodID GeckoAppShell::jIsNetworkLinkUp = 0;
 jmethodID GeckoAppShell::jIsTablet = 0;
+jmethodID GeckoAppShell::jIsUserRestricted = 0;
 jmethodID GeckoAppShell::jKillAnyZombies = 0;
 jmethodID GeckoAppShell::jLoadPluginClass = 0;
 jmethodID GeckoAppShell::jLockScreenOrientation = 0;
 jmethodID GeckoAppShell::jMarkURIVisited = 0;
 jmethodID GeckoAppShell::jMoveTaskToBack = 0;
 jmethodID GeckoAppShell::jNetworkLinkType = 0;
 jmethodID GeckoAppShell::jNotifyDefaultPrevented = 0;
 jmethodID GeckoAppShell::jNotifyIME = 0;
@@ -134,23 +136,25 @@ void GeckoAppShell::InitStubs(JNIEnv *jE
     jGetMessageWrapper = getStaticMethod("getMessage", "(II)V");
     jGetMimeTypeFromExtensionsWrapper = getStaticMethod("getMimeTypeFromExtensions", "(Ljava/lang/String;)Ljava/lang/String;");
     jGetNextMessageInListWrapper = getStaticMethod("getNextMessageInList", "(II)V");
     jGetProxyForURIWrapper = getStaticMethod("getProxyForURI", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;");
     jGetScreenDepthWrapper = getStaticMethod("getScreenDepth", "()I");
     jGetScreenOrientationWrapper = getStaticMethod("getScreenOrientation", "()S");
     jGetShowPasswordSetting = getStaticMethod("getShowPasswordSetting", "()Z");
     jGetSystemColoursWrapper = getStaticMethod("getSystemColors", "()[I");
+    jGetUserRestrictions = getStaticMethod("getUserRestrictions", "()Ljava/lang/String;");
     jHandleGeckoMessageWrapper = getStaticMethod("handleGeckoMessage", "(Lorg/mozilla/gecko/util/NativeJSContainer;)V");
     jHandleUncaughtException = getStaticMethod("handleUncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
     jHideProgressDialog = getStaticMethod("hideProgressDialog", "()V");
     jInitCameraWrapper = getStaticMethod("initCamera", "(Ljava/lang/String;III)[I");
     jIsNetworkLinkKnown = getStaticMethod("isNetworkLinkKnown", "()Z");
     jIsNetworkLinkUp = getStaticMethod("isNetworkLinkUp", "()Z");
     jIsTablet = getStaticMethod("isTablet", "()Z");
+    jIsUserRestricted = getStaticMethod("isUserRestricted", "()Z");
     jKillAnyZombies = getStaticMethod("killAnyZombies", "()V");
     jLoadPluginClass = getStaticMethod("loadPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;");
     jLockScreenOrientation = getStaticMethod("lockScreenOrientation", "(I)V");
     jMarkURIVisited = getStaticMethod("markUriVisited", "(Ljava/lang/String;)V");
     jMoveTaskToBack = getStaticMethod("moveTaskToBack", "()V");
     jNetworkLinkType = getStaticMethod("networkLinkType", "()I");
     jNotifyDefaultPrevented = getStaticMethod("notifyDefaultPrevented", "(Z)V");
     jNotifyIME = getStaticMethod("notifyIME", "(I)V");
@@ -757,16 +761,29 @@ jintArray GeckoAppShell::GetSystemColour
     }
 
     jobject temp = env->CallStaticObjectMethod(mGeckoAppShellClass, jGetSystemColoursWrapper);
     AndroidBridge::HandleUncaughtException(env);
     jintArray ret = static_cast<jintArray>(env->PopLocalFrame(temp));
     return ret;
 }
 
+jstring GeckoAppShell::GetUserRestrictions() {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallStaticObjectMethod(mGeckoAppShellClass, jGetUserRestrictions);
+    AndroidBridge::HandleUncaughtException(env);
+    jstring ret = static_cast<jstring>(env->PopLocalFrame(temp));
+    return ret;
+}
+
 void GeckoAppShell::HandleGeckoMessageWrapper(jobject a0) {
     JNIEnv *env = AndroidBridge::GetJNIEnv();
     if (env->PushLocalFrame(1) != 0) {
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jHandleGeckoMessageWrapper, a0);
@@ -849,16 +866,29 @@ bool GeckoAppShell::IsTablet() {
     }
 
     bool temp = env->CallStaticBooleanMethod(mGeckoAppShellClass, jIsTablet);
     AndroidBridge::HandleUncaughtException(env);
     env->PopLocalFrame(nullptr);
     return temp;
 }
 
+bool GeckoAppShell::IsUserRestricted() {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    bool temp = env->CallStaticBooleanMethod(mGeckoAppShellClass, jIsUserRestricted);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
 void GeckoAppShell::KillAnyZombies() {
     JNIEnv *env = AndroidBridge::GetJNIEnv();
     if (env->PushLocalFrame(0) != 0) {
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jKillAnyZombies);
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -56,23 +56,25 @@ public:
     static void GetMessageWrapper(int32_t a0, int32_t a1);
     static jstring GetMimeTypeFromExtensionsWrapper(const nsAString& a0);
     static void GetNextMessageInListWrapper(int32_t a0, int32_t a1);
     static jstring GetProxyForURIWrapper(const nsAString& a0, const nsAString& a1, const nsAString& a2, int32_t a3);
     static int32_t GetScreenDepthWrapper();
     static int16_t GetScreenOrientationWrapper();
     static bool GetShowPasswordSetting();
     static jintArray GetSystemColoursWrapper();
+    static jstring GetUserRestrictions();
     static void HandleGeckoMessageWrapper(jobject a0);
     static void HandleUncaughtException(jobject a0, jthrowable a1);
     static void HideProgressDialog();
     static jintArray InitCameraWrapper(const nsAString& a0, int32_t a1, int32_t a2, int32_t a3);
     static bool IsNetworkLinkKnown();
     static bool IsNetworkLinkUp();
     static bool IsTablet();
+    static bool IsUserRestricted();
     static void KillAnyZombies();
     static jclass LoadPluginClass(const nsAString& a0, const nsAString& a1);
     static void LockScreenOrientation(int32_t a0);
     static void MarkURIVisited(const nsAString& a0);
     static void MoveTaskToBack();
     static int32_t NetworkLinkType();
     static void NotifyDefaultPrevented(bool a0);
     static void NotifyIME(int32_t a0);
@@ -140,23 +142,25 @@ protected:
     static jmethodID jGetMessageWrapper;
     static jmethodID jGetMimeTypeFromExtensionsWrapper;
     static jmethodID jGetNextMessageInListWrapper;
     static jmethodID jGetProxyForURIWrapper;
     static jmethodID jGetScreenDepthWrapper;
     static jmethodID jGetScreenOrientationWrapper;
     static jmethodID jGetShowPasswordSetting;
     static jmethodID jGetSystemColoursWrapper;
+    static jmethodID jGetUserRestrictions;
     static jmethodID jHandleGeckoMessageWrapper;
     static jmethodID jHandleUncaughtException;
     static jmethodID jHideProgressDialog;
     static jmethodID jInitCameraWrapper;
     static jmethodID jIsNetworkLinkKnown;
     static jmethodID jIsNetworkLinkUp;
     static jmethodID jIsTablet;
+    static jmethodID jIsUserRestricted;
     static jmethodID jKillAnyZombies;
     static jmethodID jLoadPluginClass;
     static jmethodID jLockScreenOrientation;
     static jmethodID jMarkURIVisited;
     static jmethodID jMoveTaskToBack;
     static jmethodID jNetworkLinkType;
     static jmethodID jNotifyDefaultPrevented;
     static jmethodID jNotifyIME;