Home | History | Annotate | Download | only in browser
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.browser;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.database.ContentObserver;
     22 import android.net.Uri;
     23 import android.os.AsyncTask;
     24 import android.os.Handler;
     25 import android.preference.PreferenceManager;
     26 import android.provider.Settings;
     27 import android.text.TextUtils;
     28 import android.webkit.GeolocationPermissions;
     29 import android.webkit.ValueCallback;
     30 
     31 import java.util.HashSet;
     32 import java.util.Set;
     33 
     34 /**
     35  * Manages the interaction between the secure system setting for default geolocation
     36  * permissions and the browser.
     37  */
     38 class SystemAllowGeolocationOrigins {
     39 
     40     // Preference key for the value of the system setting last read by the browser
     41     private final static String LAST_READ_ALLOW_GEOLOCATION_ORIGINS =
     42             "last_read_allow_geolocation_origins";
     43 
     44     // The application context
     45     private final Context mContext;
     46 
     47     // The observer used to listen to the system setting.
     48     private final SettingObserver mSettingObserver;
     49 
     50     public SystemAllowGeolocationOrigins(Context context) {
     51         mContext = context.getApplicationContext();
     52         mSettingObserver = new SettingObserver();
     53     }
     54 
     55     /**
     56      * Checks whether the setting has changed and installs an observer to listen for
     57      * future changes. Must be called on the application main thread.
     58      */
     59     public void start() {
     60         // Register to receive notifications when the system settings change.
     61         Uri uri = Settings.Secure.getUriFor(Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS);
     62         mContext.getContentResolver().registerContentObserver(uri, false, mSettingObserver);
     63 
     64         // Read and apply the setting if needed.
     65         maybeApplySettingAsync();
     66     }
     67 
     68     /**
     69      * Stops the manager.
     70      */
     71     public void stop() {
     72         mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
     73     }
     74 
     75     void maybeApplySettingAsync() {
     76         BackgroundHandler.execute(mMaybeApplySetting);
     77     }
     78 
     79     /**
     80      * Checks to see if the system setting has changed and if so,
     81      * updates the Geolocation permissions accordingly.
     82      */
     83     private Runnable mMaybeApplySetting = new Runnable() {
     84 
     85         @Override
     86         public void run() {
     87          // Get the new value
     88             String newSetting = getSystemSetting();
     89 
     90             // Get the last read value
     91             SharedPreferences preferences = BrowserSettings.getInstance()
     92                     .getPreferences();
     93             String lastReadSetting =
     94                     preferences.getString(LAST_READ_ALLOW_GEOLOCATION_ORIGINS, "");
     95 
     96             // If the new value is the same as the last one we read, we're done.
     97             if (TextUtils.equals(lastReadSetting, newSetting)) {
     98                 return;
     99             }
    100 
    101             // Save the new value as the last read value
    102             preferences.edit()
    103                     .putString(LAST_READ_ALLOW_GEOLOCATION_ORIGINS, newSetting)
    104                     .apply();
    105 
    106             Set<String> oldOrigins = parseAllowGeolocationOrigins(lastReadSetting);
    107             Set<String> newOrigins = parseAllowGeolocationOrigins(newSetting);
    108             Set<String> addedOrigins = setMinus(newOrigins, oldOrigins);
    109             Set<String> removedOrigins = setMinus(oldOrigins, newOrigins);
    110 
    111             // Remove the origins in the last read value
    112             removeOrigins(removedOrigins);
    113 
    114             // Add the origins in the new value
    115             addOrigins(addedOrigins);
    116         }
    117     };
    118 
    119     /**
    120      * Parses the value of the default geolocation permissions setting.
    121      *
    122      * @param setting A space-separated list of origins.
    123      * @return A mutable set of origins.
    124      */
    125     private static HashSet<String> parseAllowGeolocationOrigins(String setting) {
    126         HashSet<String> origins = new HashSet<String>();
    127         if (!TextUtils.isEmpty(setting)) {
    128             for (String origin : setting.split("\\s+")) {
    129                 if (!TextUtils.isEmpty(origin)) {
    130                     origins.add(origin);
    131                 }
    132             }
    133         }
    134         return origins;
    135     }
    136 
    137     /**
    138      * Gets the difference between two sets. Does not modify any of the arguments.
    139      *
    140      * @return A set containing all elements in {@code x} that are not in {@code y}.
    141      */
    142     private <A> Set<A> setMinus(Set<A> x, Set<A> y) {
    143         HashSet<A> z = new HashSet<A>(x.size());
    144         for (A a : x) {
    145             if (!y.contains(a)) {
    146                 z.add(a);
    147             }
    148         }
    149         return z;
    150     }
    151 
    152     /**
    153      * Gets the current system setting for default allowed geolocation origins.
    154      *
    155      * @return The default allowed origins. Returns {@code ""} if not set.
    156      */
    157     private String getSystemSetting() {
    158         String value = Settings.Secure.getString(mContext.getContentResolver(),
    159                 Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS);
    160         return value == null ? "" : value;
    161     }
    162 
    163     /**
    164      * Adds geolocation permissions for the given origins.
    165      */
    166     private void addOrigins(Set<String> origins) {
    167         for (String origin : origins) {
    168             GeolocationPermissions.getInstance().allow(origin);
    169         }
    170     }
    171 
    172     /**
    173      * Removes geolocation permissions for the given origins, if they are allowed.
    174      * If they are denied or not set, nothing is done.
    175      */
    176     private void removeOrigins(Set<String> origins) {
    177         for (final String origin : origins) {
    178             GeolocationPermissions.getInstance().getAllowed(origin, new ValueCallback<Boolean>() {
    179                 public void onReceiveValue(Boolean value) {
    180                     if (value != null && value.booleanValue()) {
    181                         GeolocationPermissions.getInstance().clear(origin);
    182                     }
    183                 }
    184             });
    185         }
    186     }
    187 
    188     /**
    189      * Listens for changes to the system setting.
    190      */
    191     private class SettingObserver extends ContentObserver {
    192 
    193         SettingObserver() {
    194             super(new Handler());
    195         }
    196 
    197         @Override
    198         public void onChange(boolean selfChange) {
    199             maybeApplySettingAsync();
    200         }
    201     }
    202 
    203 }
    204