blob: e0759ade785cf031061ccc3dcf77db2b8684e57b [file] [log] [blame]
/*
* Copyright (C) 2010 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 com.android.browser;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.webkit.GeolocationPermissions;
import android.webkit.ValueCallback;
import java.util.HashSet;
import java.util.Set;
/**
* Manages the interaction between the secure system setting for default geolocation
* permissions and the browser.
*/
class SystemAllowGeolocationOrigins {
// Preference key for the value of the system setting last read by the browser
private final static String LAST_READ_ALLOW_GEOLOCATION_ORIGINS =
"last_read_allow_geolocation_origins";
// The application context
private final Context mContext;
// The observer used to listen to the system setting.
private final SettingObserver mSettingObserver;
public SystemAllowGeolocationOrigins(Context context) {
mContext = context.getApplicationContext();
mSettingObserver = new SettingObserver();
}
/**
* Checks whether the setting has changed and installs an observer to listen for
* future changes. Must be called on the application main thread.
*/
public void start() {
// Register to receive notifications when the system settings change.
Uri uri = Settings.Secure.getUriFor(Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS);
mContext.getContentResolver().registerContentObserver(uri, false, mSettingObserver);
// Read and apply the setting if needed.
maybeApplySettingAsync();
}
/**
* Stops the manager.
*/
public void stop() {
mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
}
void maybeApplySettingAsync() {
BackgroundHandler.execute(mMaybeApplySetting);
}
/**
* Checks to see if the system setting has changed and if so,
* updates the Geolocation permissions accordingly.
*/
private Runnable mMaybeApplySetting = new Runnable() {
@Override
public void run() {
// Get the new value
String newSetting = getSystemSetting();
// Get the last read value
SharedPreferences preferences = BrowserSettings.getInstance()
.getPreferences();
String lastReadSetting =
preferences.getString(LAST_READ_ALLOW_GEOLOCATION_ORIGINS, "");
// If the new value is the same as the last one we read, we're done.
if (TextUtils.equals(lastReadSetting, newSetting)) {
return;
}
// Save the new value as the last read value
preferences.edit()
.putString(LAST_READ_ALLOW_GEOLOCATION_ORIGINS, newSetting)
.apply();
Set<String> oldOrigins = parseAllowGeolocationOrigins(lastReadSetting);
Set<String> newOrigins = parseAllowGeolocationOrigins(newSetting);
Set<String> addedOrigins = setMinus(newOrigins, oldOrigins);
Set<String> removedOrigins = setMinus(oldOrigins, newOrigins);
// Remove the origins in the last read value
removeOrigins(removedOrigins);
// Add the origins in the new value
addOrigins(addedOrigins);
}
};
/**
* Parses the value of the default geolocation permissions setting.
*
* @param setting A space-separated list of origins.
* @return A mutable set of origins.
*/
private static HashSet<String> parseAllowGeolocationOrigins(String setting) {
HashSet<String> origins = new HashSet<String>();
if (!TextUtils.isEmpty(setting)) {
for (String origin : setting.split("\\s+")) {
if (!TextUtils.isEmpty(origin)) {
origins.add(origin);
}
}
}
return origins;
}
/**
* Gets the difference between two sets. Does not modify any of the arguments.
*
* @return A set containing all elements in {@code x} that are not in {@code y}.
*/
private <A> Set<A> setMinus(Set<A> x, Set<A> y) {
HashSet<A> z = new HashSet<A>(x.size());
for (A a : x) {
if (!y.contains(a)) {
z.add(a);
}
}
return z;
}
/**
* Gets the current system setting for default allowed geolocation origins.
*
* @return The default allowed origins. Returns {@code ""} if not set.
*/
private String getSystemSetting() {
String value = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS);
return value == null ? "" : value;
}
/**
* Adds geolocation permissions for the given origins.
*/
private void addOrigins(Set<String> origins) {
for (String origin : origins) {
GeolocationPermissions.getInstance().allow(origin);
}
}
/**
* Removes geolocation permissions for the given origins, if they are allowed.
* If they are denied or not set, nothing is done.
*/
private void removeOrigins(Set<String> origins) {
for (final String origin : origins) {
GeolocationPermissions.getInstance().getAllowed(origin, new ValueCallback<Boolean>() {
public void onReceiveValue(Boolean value) {
if (value != null && value.booleanValue()) {
GeolocationPermissions.getInstance().clear(origin);
}
}
});
}
}
/**
* Listens for changes to the system setting.
*/
private class SettingObserver extends ContentObserver {
SettingObserver() {
super(new Handler());
}
@Override
public void onChange(boolean selfChange) {
maybeApplySettingAsync();
}
}
}