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