diff --git a/mobile/android/base/java/org/mozilla/gecko/Restrictions.java b/mobile/android/base/java/org/mozilla/gecko/Restrictions.java index 54079a451bdc..30ec53bd9b3b 100644 --- a/mobile/android/base/java/org/mozilla/gecko/Restrictions.java +++ b/mobile/android/base/java/org/mozilla/gecko/Restrictions.java @@ -5,21 +5,21 @@ package org.mozilla.gecko; +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; +import android.util.Log; + +import org.mozilla.gecko.AppConstants.Versions; import org.mozilla.gecko.annotation.RobocopTarget; import org.mozilla.gecko.annotation.WrapForJNI; -import org.mozilla.gecko.AppConstants.Versions; import org.mozilla.gecko.restrictions.DefaultConfiguration; import org.mozilla.gecko.restrictions.GuestProfileConfiguration; import org.mozilla.gecko.restrictions.Restrictable; import org.mozilla.gecko.restrictions.RestrictedProfileConfiguration; +import org.mozilla.gecko.restrictions.RestrictionCache; import org.mozilla.gecko.restrictions.RestrictionConfiguration; -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.os.UserManager; -import android.util.Log; - @RobocopTarget public class Restrictions { private static final String LOGTAG = "GeckoRestrictedProfiles"; @@ -74,8 +74,7 @@ public class Restrictions { } // The user is on a restricted profile if, and only if, we injected application restrictions during account setup. - final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE); - return !mgr.getApplicationRestrictions(context.getPackageName()).isEmpty(); + return RestrictionCache.hasApplicationRestrictions(context); } public static void update(Context context) { diff --git a/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictedProfileConfiguration.java b/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictedProfileConfiguration.java index e47bcf601028..d6c5c1e5caaa 100644 --- a/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictedProfileConfiguration.java +++ b/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictedProfileConfiguration.java @@ -66,9 +66,6 @@ public class RestrictedProfileConfiguration implements RestrictionConfiguration } private Context context; - private Bundle cachedAppRestrictions; - private Bundle cachedUserRestrictions; - private boolean isCacheInvalid = true; public RestrictedProfileConfiguration(Context context) { this.context = context.getApplicationContext(); @@ -76,38 +73,17 @@ public class RestrictedProfileConfiguration implements RestrictionConfiguration @Override public synchronized boolean isAllowed(Restrictable restrictable) { - if (isCacheInvalid || !ThreadUtils.isOnUiThread()) { - readRestrictions(); - isCacheInvalid = false; - } - // Special casing system/user restrictions if (restrictable == Restrictable.INSTALL_APPS || restrictable == Restrictable.MODIFY_ACCOUNTS) { - return !cachedUserRestrictions.getBoolean(restrictable.name); + return RestrictionCache.getUserRestriction(context, restrictable.name); } - if (!cachedAppRestrictions.containsKey(restrictable.name) && !configuration.containsKey(restrictable)) { + if (!RestrictionCache.hasApplicationRestriction(context, restrictable.name) && !configuration.containsKey(restrictable)) { // Always allow features that are not in the configuration return true; } - return cachedAppRestrictions.getBoolean(restrictable.name, configuration.get(restrictable)); - } - - private void readRestrictions() { - final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE); - - StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads(); - - try { - Bundle appRestrictions = mgr.getApplicationRestrictions(context.getPackageName()); - migrateRestrictionsIfNeeded(appRestrictions); - - cachedAppRestrictions = appRestrictions; - cachedUserRestrictions = mgr.getUserRestrictions(); - } finally { - StrictMode.setThreadPolicy(policy); - } + return RestrictionCache.getApplicationRestriction(context, restrictable.name, configuration.get(restrictable)); } @Override @@ -135,7 +111,7 @@ public class RestrictedProfileConfiguration implements RestrictionConfiguration @Override public synchronized void update() { - isCacheInvalid = true; + RestrictionCache.invalidate(); } public static List getVisibleRestrictions() { @@ -150,25 +126,4 @@ public class RestrictedProfileConfiguration implements RestrictionConfiguration return visibleList; } - - /** - * This method migrates the old set of DISALLOW_ restrictions to the new restrictable feature ones (Bug 1189336). - */ - public static void migrateRestrictionsIfNeeded(Bundle bundle) { - if (!bundle.containsKey(Restrictable.INSTALL_EXTENSION.name) && bundle.containsKey("no_install_extensions")) { - bundle.putBoolean(Restrictable.INSTALL_EXTENSION.name, !bundle.getBoolean("no_install_extensions")); - } - - if (!bundle.containsKey(Restrictable.PRIVATE_BROWSING.name) && bundle.containsKey("no_private_browsing")) { - bundle.putBoolean(Restrictable.PRIVATE_BROWSING.name, !bundle.getBoolean("no_private_browsing")); - } - - if (!bundle.containsKey(Restrictable.CLEAR_HISTORY.name) && bundle.containsKey("no_clear_history")) { - bundle.putBoolean(Restrictable.CLEAR_HISTORY.name, !bundle.getBoolean("no_clear_history")); - } - - if (!bundle.containsKey(Restrictable.ADVANCED_SETTINGS.name) && bundle.containsKey("no_advanced_settings")) { - bundle.putBoolean(Restrictable.ADVANCED_SETTINGS.name, !bundle.getBoolean("no_advanced_settings")); - } - } } diff --git a/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionCache.java b/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionCache.java new file mode 100644 index 000000000000..523cc113b4c9 --- /dev/null +++ b/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionCache.java @@ -0,0 +1,99 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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.restrictions; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.os.StrictMode; +import android.os.UserManager; + +import org.mozilla.gecko.util.ThreadUtils; + +/** + * Cache for user and application restrictions. + */ +public class RestrictionCache { + private static Bundle cachedAppRestrictions; + private static Bundle cachedUserRestrictions; + private static boolean isCacheInvalid = true; + + private RestrictionCache() {} + + public static synchronized boolean getUserRestriction(Context context, String restriction) { + updateCacheIfNeeded(context); + return cachedUserRestrictions.getBoolean(restriction); + } + + public static synchronized boolean hasApplicationRestriction(Context context, String restriction) { + updateCacheIfNeeded(context); + return cachedAppRestrictions.containsKey(restriction); + } + + public static synchronized boolean getApplicationRestriction(Context context, String restriction, boolean defaultValue) { + updateCacheIfNeeded(context); + return cachedAppRestrictions.getBoolean(restriction, defaultValue); + } + + public static synchronized boolean hasApplicationRestrictions(Context context) { + updateCacheIfNeeded(context); + return !cachedAppRestrictions.isEmpty(); + } + + public static synchronized void invalidate() { + isCacheInvalid = true; + } + + private static void updateCacheIfNeeded(Context context) { + // If we are not on the UI thread then we can just go ahead and read the values (Bug 1189347). + // Otherwise we read from the cache to avoid blocking the UI thread. If the cache is invalid + // then we hazard the consequences and just do the read. + if (isCacheInvalid || !ThreadUtils.isOnUiThread()) { + readRestrictions(context); + isCacheInvalid = false; + } + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) + private static void readRestrictions(Context context) { + final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE); + + // If we do not have anything in the cache yet then this read might happen on the UI thread (Bug 1189347). + final StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads(); + + try { + Bundle appRestrictions = mgr.getApplicationRestrictions(context.getPackageName()); + migrateRestrictionsIfNeeded(appRestrictions); + + cachedAppRestrictions = appRestrictions; + cachedUserRestrictions = mgr.getUserRestrictions(); // Always implies disk read + } finally { + StrictMode.setThreadPolicy(policy); + } + } + + /** + * This method migrates the old set of DISALLOW_ restrictions to the new restrictable feature ones (Bug 1189336). + */ + /* package-private */ static void migrateRestrictionsIfNeeded(Bundle bundle) { + if (!bundle.containsKey(Restrictable.INSTALL_EXTENSION.name) && bundle.containsKey("no_install_extensions")) { + bundle.putBoolean(Restrictable.INSTALL_EXTENSION.name, !bundle.getBoolean("no_install_extensions")); + } + + if (!bundle.containsKey(Restrictable.PRIVATE_BROWSING.name) && bundle.containsKey("no_private_browsing")) { + bundle.putBoolean(Restrictable.PRIVATE_BROWSING.name, !bundle.getBoolean("no_private_browsing")); + } + + if (!bundle.containsKey(Restrictable.CLEAR_HISTORY.name) && bundle.containsKey("no_clear_history")) { + bundle.putBoolean(Restrictable.CLEAR_HISTORY.name, !bundle.getBoolean("no_clear_history")); + } + + if (!bundle.containsKey(Restrictable.ADVANCED_SETTINGS.name) && bundle.containsKey("no_advanced_settings")) { + bundle.putBoolean(Restrictable.ADVANCED_SETTINGS.name, !bundle.getBoolean("no_advanced_settings")); + } + } +} diff --git a/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionProvider.java b/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionProvider.java index 735504e70ed8..26b9a446f2f9 100644 --- a/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionProvider.java +++ b/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionProvider.java @@ -38,7 +38,7 @@ public class RestrictionProvider extends BroadcastReceiver { @Override public void run() { final Bundle oldRestrictions = intent.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE); - RestrictedProfileConfiguration.migrateRestrictionsIfNeeded(oldRestrictions); + RestrictionCache.migrateRestrictionsIfNeeded(oldRestrictions); final Bundle extras = new Bundle(); diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 04da2cf03141..f1f93ec42629 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -505,6 +505,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [ 'restrictions/GuestProfileConfiguration.java', 'restrictions/Restrictable.java', 'restrictions/RestrictedProfileConfiguration.java', + 'restrictions/RestrictionCache.java', 'restrictions/RestrictionConfiguration.java', 'restrictions/RestrictionProvider.java', 'ScreenshotObserver.java',