From d875ce6dac3c2e9a671c121c80b40d2536cbb2af Mon Sep 17 00:00:00 2001 From: Steve Block Date: Mon, 24 Aug 2009 11:31:18 +0100 Subject: [PATCH] Sets or clears Geolocation permissions for Google origins when the 'Location & privacy - Share with Google' sysetm setting is changed. This fixes bug http://b/issue?id=1933893 --- .../android/GoogleLocationSettingManager.java | 156 ++++++++++++++++++ .../webkit/GeolocationPermissions.java | 63 ++++++- core/java/android/webkit/WebSettings.java | 6 + 3 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 core/java/android/GoogleLocationSettingManager.java diff --git a/core/java/android/GoogleLocationSettingManager.java b/core/java/android/GoogleLocationSettingManager.java new file mode 100644 index 0000000000000..fe7fce6b88b70 --- /dev/null +++ b/core/java/android/GoogleLocationSettingManager.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.database.ContentObserver; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.provider.Settings; + +import java.util.HashSet; + +/** + * A class to manage the interaction between the system setting 'Location & + * Security - Share with Google' and the browser. When this setting is set + * to true, we allow Geolocation for Google origins. When this setting is + * set to false, we clear Geolocation permissions for Google origins. + * @hide pending API council review + */ +class GoogleLocationSettingManager { + // The application context. + private Context mContext; + // The observer used to listen to the system setting. + private GoogleLocationSettingObserver mSettingObserver; + + // The value of the system setting that indicates true. + private final static int sSystemSettingTrue = 1; + // The value of the system setting that indicates false. + private final static int sSystemSettingFalse = 0; + // The value of the USE_LOCATION_FOR_SERVICES system setting last read + // by the browser. + private final static String LAST_READ_USE_LOCATION_FOR_SERVICES = + "lastReadUseLocationForServices"; + // The Google origins we consider. + private static HashSet sGoogleOrigins; + static { + sGoogleOrigins = new HashSet(); + // NOTE: DO NOT ADD A "/" AT THE END! + sGoogleOrigins.add("http://www.google.com"); + sGoogleOrigins.add("http://www.google.co.uk"); + } + + GoogleLocationSettingManager(Context context) { + mContext = context; + } + + /** + * Starts the manager. Checks whether the setting has changed and + * installs an observer to listen for future changes. + */ + public void start() { + maybeApplySetting(); + + mSettingObserver = new GoogleLocationSettingObserver(); + mSettingObserver.observe(); + } + + /** + * Checks to see if the system setting has changed and if so, + * updates the Geolocation permissions accordingly. + */ + private void maybeApplySetting() { + int setting = getSystemSetting(); + if (settingChanged(setting)) { + applySetting(setting); + } + } + + /** + * Gets the current system setting for 'Use location for Google services'. + * @return The system setting. + */ + private int getSystemSetting() { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.USE_LOCATION_FOR_SERVICES, + sSystemSettingFalse); + } + + /** + * Determines whether the supplied setting has changed from the last + * value read by the browser. + * @param setting The setting. + * @return Whether the setting has changed from the last value read + * by the browser. + */ + private boolean settingChanged(int setting) { + SharedPreferences preferences = + PreferenceManager.getDefaultSharedPreferences(mContext); + // Default to false. If the system setting is false the first time it is ever read by the + // browser, there's nothing to do. + int lastReadSetting = sSystemSettingFalse; + lastReadSetting = preferences.getInt(LAST_READ_USE_LOCATION_FOR_SERVICES, + lastReadSetting); + + if (lastReadSetting == setting) { + return false; + } + + Editor editor = preferences.edit(); + editor.putInt(LAST_READ_USE_LOCATION_FOR_SERVICES, setting); + editor.commit(); + return true; + } + + /** + * Applies the supplied setting to the Geolocation permissions. + * @param setting The setting. + */ + private void applySetting(int setting) { + for (String origin : sGoogleOrigins) { + if (setting == sSystemSettingTrue) { + GeolocationPermissions.getInstance().allow(origin); + } else { + GeolocationPermissions.getInstance().clear(origin); + } + } + } + + /** + * This class implements an observer to listen for changes to the + * system setting. + */ + class GoogleLocationSettingObserver extends ContentObserver { + GoogleLocationSettingObserver() { + super(new Handler()); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.USE_LOCATION_FOR_SERVICES), false, this); + } + + @Override + public void onChange(boolean selfChange) { + maybeApplySetting(); + } + } +} diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java index d06d7e2e9f0fd..e985ccce68315 100755 --- a/core/java/android/webkit/GeolocationPermissions.java +++ b/core/java/android/webkit/GeolocationPermissions.java @@ -22,6 +22,7 @@ import android.util.Log; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import java.util.HashSet; import java.util.Set; @@ -50,6 +51,8 @@ public final class GeolocationPermissions { // Members used to transfer the origins and permissions between threads. private Set mOrigins; private boolean mAllowed; + private Set mOriginsToClear; + private Set mOriginsToAllow; private static Lock mLock = new ReentrantLock(); private static boolean mUpdated; private static Condition mUpdatedCondition = mLock.newCondition(); @@ -58,7 +61,8 @@ public final class GeolocationPermissions { static final int GET_ORIGINS = 0; static final int GET_ALLOWED = 1; static final int CLEAR = 2; - static final int CLEAR_ALL = 3; + static final int ALLOW = 3; + static final int CLEAR_ALL = 4; /** * Gets the singleton instance of the class. @@ -74,6 +78,7 @@ public final class GeolocationPermissions { * Creates the message handler. Must be called on the WebKit thread. */ public void createHandler() { + mLock.lock(); if (mHandler == null) { mHandler = new Handler() { @Override @@ -89,13 +94,28 @@ public final class GeolocationPermissions { case CLEAR: nativeClear((String) msg.obj); break; + case ALLOW: + nativeAllow((String) msg.obj); + break; case CLEAR_ALL: nativeClearAll(); break; } } }; + + if (mOriginsToClear != null) { + for (String origin : mOriginsToClear) { + nativeClear(origin); + } + } + if (mOriginsToAllow != null) { + for (String origin : mOriginsToAllow) { + nativeAllow(origin); + } + } } + mLock.unlock(); } /** @@ -179,11 +199,47 @@ public final class GeolocationPermissions { } /** - * Clears the permission state for the specified origin. + * Clears the permission state for the specified origin. This method may be + * called before the WebKit thread has intialized the message handler. + * Messages will be queued until this time. */ public void clear(String origin) { // Called on the UI thread. - postMessage(Message.obtain(null, CLEAR, origin)); + mLock.lock(); + if (mHandler == null) { + if (mOriginsToClear == null) { + mOriginsToClear = new HashSet(); + } + mOriginsToClear.add(origin); + if (mOriginsToAllow != null) { + mOriginsToAllow.remove(origin); + } + } else { + postMessage(Message.obtain(null, CLEAR, origin)); + } + mLock.unlock(); + } + + /** + * Allows the specified origin. This method may be called before the WebKit + * thread has intialized the message handler. Messages will be queued until + * this time. + */ + public void allow(String origin) { + // Called on the UI thread. + mLock.lock(); + if (mHandler == null) { + if (mOriginsToAllow == null) { + mOriginsToAllow = new HashSet(); + } + mOriginsToAllow.add(origin); + if (mOriginsToClear != null) { + mOriginsToClear.remove(origin); + } + } else { + postMessage(Message.obtain(null, ALLOW, origin)); + } + mLock.unlock(); } /** @@ -198,5 +254,6 @@ public final class GeolocationPermissions { private static native Set nativeGetOrigins(); private static native boolean nativeGetAllowed(String origin); private static native void nativeClear(String origin); + private static native void nativeAllow(String origin); private static native void nativeClearAll(); } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index e8b2702f5bc5e..3793dd5f67fd5 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -189,6 +189,10 @@ public class WebSettings { private boolean mAllowFileAccess = true; private boolean mLoadWithOverviewMode = true; + // Manages interaction of the system setting 'Location & security - Share + // with Google' and the browser. + static GoogleLocationSettingManager sGoogleLocationSettingManager; + // Class to handle messages before WebCore is ready. private class EventHandler { // Message id for syncing @@ -1315,6 +1319,8 @@ public class WebSettings { if (DebugFlags.WEB_SETTINGS) { junit.framework.Assert.assertTrue(frame.mNativeFrame != 0); } + sGoogleLocationSettingManager = new GoogleLocationSettingManager(mContext); + sGoogleLocationSettingManager.start(); nativeSync(frame.mNativeFrame); mSyncPending = false; mEventHandler.createHandler();