1/*
2 * Copyright (C) 2012 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
18package com.android.server.location;
19
20import android.content.Context;
21import android.database.ContentObserver;
22import android.os.Handler;
23import android.os.UserHandle;
24import android.provider.Settings;
25import android.util.Log;
26import android.util.Slog;
27
28import com.android.server.LocationManagerService;
29
30import java.io.PrintWriter;
31import java.util.ArrayList;
32import java.util.Arrays;
33
34/**
35 * Allows applications to be blacklisted from location updates at run-time.
36 *
37 * This is a silent blacklist. Applications can still call Location Manager
38 * API's, but they just won't receive any locations.
39 */
40public final class LocationBlacklist extends ContentObserver {
41    private static final String TAG = "LocationBlacklist";
42    private static final boolean D = LocationManagerService.D;
43    private static final String BLACKLIST_CONFIG_NAME = "locationPackagePrefixBlacklist";
44    private static final String WHITELIST_CONFIG_NAME = "locationPackagePrefixWhitelist";
45
46    private final Context mContext;
47    private final Object mLock = new Object();
48
49    // all fields below synchronized on mLock
50    private String[] mWhitelist = new String[0];
51    private String[] mBlacklist = new String[0];
52
53    private int mCurrentUserId = UserHandle.USER_OWNER;
54
55    public LocationBlacklist(Context context, Handler handler) {
56        super(handler);
57        mContext = context;
58    }
59
60    public void init() {
61        mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
62                BLACKLIST_CONFIG_NAME), false, this, UserHandle.USER_ALL);
63//        mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
64//                WHITELIST_CONFIG_NAME), false, this, UserHandle.USER_ALL);
65        reloadBlacklist();
66    }
67
68    private void reloadBlacklistLocked() {
69        mWhitelist = getStringArrayLocked(WHITELIST_CONFIG_NAME);
70        Slog.i(TAG, "whitelist: " + Arrays.toString(mWhitelist));
71        mBlacklist = getStringArrayLocked(BLACKLIST_CONFIG_NAME);
72        Slog.i(TAG, "blacklist: " + Arrays.toString(mBlacklist));
73    }
74
75    private void reloadBlacklist() {
76        synchronized (mLock) {
77            reloadBlacklistLocked();
78        }
79    }
80
81    /**
82     * Return true if in blacklist
83     * (package name matches blacklist, and does not match whitelist)
84     */
85    public boolean isBlacklisted(String packageName) {
86        synchronized (mLock) {
87            for (String black : mBlacklist) {
88                if (packageName.startsWith(black)) {
89                    if (inWhitelist(packageName)) {
90                        continue;
91                    } else {
92                        if (D) Log.d(TAG, "dropping location (blacklisted): "
93                                + packageName + " matches " + black);
94                        return true;
95                    }
96                }
97            }
98        }
99        return false;
100    }
101
102    /**
103     * Return true if any of packages are in whitelist
104     */
105    private boolean inWhitelist(String pkg) {
106        synchronized (mLock) {
107            for (String white : mWhitelist) {
108                if (pkg.startsWith(white)) return true;
109            }
110        }
111        return false;
112    }
113
114    @Override
115    public void onChange(boolean selfChange) {
116        reloadBlacklist();
117    }
118
119    public void switchUser(int userId) {
120        synchronized(mLock) {
121            mCurrentUserId = userId;
122            reloadBlacklistLocked();
123        }
124    }
125
126    private String[] getStringArrayLocked(String key) {
127        String flatString;
128        synchronized(mLock) {
129            flatString = Settings.Secure.getStringForUser(mContext.getContentResolver(), key,
130                    mCurrentUserId);
131        }
132        if (flatString == null) {
133            return new String[0];
134        }
135        String[] splitStrings = flatString.split(",");
136        ArrayList<String> result = new ArrayList<String>();
137        for (String pkg : splitStrings) {
138            pkg = pkg.trim();
139            if (pkg.isEmpty()) {
140                continue;
141            }
142            result.add(pkg);
143        }
144        return result.toArray(new String[result.size()]);
145    }
146
147    public void dump(PrintWriter pw) {
148        pw.println("mWhitelist=" + Arrays.toString(mWhitelist) + " mBlacklist=" +
149                Arrays.toString(mBlacklist));
150    }
151}
152