/* * 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(); } } }