1e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill/*
2e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * Copyright (C) 2013 The Android Open Source Project
3e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill *
4e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * Licensed under the Apache License, Version 2.0 (the "License");
5e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * you may not use this file except in compliance with the License.
6e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * You may obtain a copy of the License at
7e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill *
8e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill *      http://www.apache.org/licenses/LICENSE-2.0
9e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill *
10e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * Unless required by applicable law or agreed to in writing, software
11e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * distributed under the License is distributed on an "AS IS" BASIS,
12e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * See the License for the specific language governing permissions and
14e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * limitations under the License.
15e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill */
16e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
17e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillpackage com.android.settings.location;
18e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
19e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.content.Context;
20e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.content.Intent;
21421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neillimport android.content.pm.ApplicationInfo;
22e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.content.pm.PackageManager;
23e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.content.pm.ResolveInfo;
24e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.content.pm.ServiceInfo;
25e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.content.res.Resources;
26e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.content.res.TypedArray;
27e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.content.res.XmlResourceParser;
28e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.graphics.drawable.Drawable;
29e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.location.SettingInjectorService;
30e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.os.Bundle;
31e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.os.Handler;
32e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.os.Message;
33e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.os.Messenger;
341810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neillimport android.os.SystemClock;
35e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.preference.Preference;
36e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.util.AttributeSet;
37e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.util.Log;
38e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport android.util.Xml;
39e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neillimport com.android.settings.R;
40e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport org.xmlpull.v1.XmlPullParser;
41e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport org.xmlpull.v1.XmlPullParserException;
42e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
43e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport java.io.IOException;
44e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport java.util.ArrayList;
45e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neillimport java.util.HashSet;
46e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neillimport java.util.Iterator;
47e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillimport java.util.List;
48e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neillimport java.util.Set;
49e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
50e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill/**
51e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * Adds the preferences specified by the {@link InjectedSetting} objects to a preference group.
52e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill *
53e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}. We do not use that
54e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * class directly because it is not a good match for our use case: we do not need the caching, and
55e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * so do not want the additional resource hit at app install/upgrade time; and we would have to
56e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * suppress the tie-breaking between multiple services reporting settings with the same name.
57e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * Code-sharing would require extracting {@link
58e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * android.content.pm.RegisteredServicesCache#parseServiceAttributes(android.content.res.Resources,
59e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill * String, android.util.AttributeSet)} into an interface, which didn't seem worth it.
60e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill */
61e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neillclass SettingsInjector {
62e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    static final String TAG = "SettingsInjector";
63e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
64f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill    /**
65f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill     * If reading the status of a setting takes longer than this, we go ahead and start reading
66f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill     * the next setting.
67f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill     */
6825518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang    private static final long INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS = 1000;
6925518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang
70e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    /**
71e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * {@link Message#what} value for starting to load status values
72e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * in case we aren't already in the process of loading them.
73e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     */
74e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    private static final int WHAT_RELOAD = 1;
75e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
76e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    /**
77e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * {@link Message#what} value sent after receiving a status message.
78e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     */
79e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    private static final int WHAT_RECEIVED_STATUS = 2;
80e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
81e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    /**
82e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * {@link Message#what} value sent after the timeout waiting for a status message.
83e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     */
84e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    private static final int WHAT_TIMEOUT = 3;
85e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
86e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    private final Context mContext;
87e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
88e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    /**
89e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * The settings that were injected
90e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     */
91e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    private final Set<Setting> mSettings;
92e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
93e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    private final Handler mHandler;
9432e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang
9532e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang    public SettingsInjector(Context context) {
9632e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang        mContext = context;
97e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        mSettings = new HashSet<Setting>();
98e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        mHandler = new StatusLoadingHandler();
9932e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang    }
10032e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang
101e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    /**
102e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     * Returns a list with one {@link InjectedSetting} object for each {@link android.app.Service}
1031810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill     * that responds to {@link SettingInjectorService#ACTION_SERVICE_INTENT} and provides the
1041810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill     * expected setting metadata.
105e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     *
106e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
107e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     *
108e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     * TODO: unit test
109e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     */
11032e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang    private List<InjectedSetting> getSettings() {
11132e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang        PackageManager pm = mContext.getPackageManager();
1121810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill        Intent intent = new Intent(SettingInjectorService.ACTION_SERVICE_INTENT);
113e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
114e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        List<ResolveInfo> resolveInfos =
1151810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
116e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        if (Log.isLoggable(TAG, Log.DEBUG)) {
117e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            Log.d(TAG, "Found services: " + resolveInfos);
118e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        }
119e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        List<InjectedSetting> settings = new ArrayList<InjectedSetting>(resolveInfos.size());
1201810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill        for (ResolveInfo resolveInfo : resolveInfos) {
121e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            try {
1221810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                InjectedSetting setting = parseServiceInfo(resolveInfo, pm);
1231810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                if (setting == null) {
1241810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                    Log.w(TAG, "Unable to load service info " + resolveInfo);
125e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill                } else {
1261810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                    settings.add(setting);
127e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill                }
128e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            } catch (XmlPullParserException e) {
1291810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                Log.w(TAG, "Unable to load service info " + resolveInfo, e);
130e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            } catch (IOException e) {
1311810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                Log.w(TAG, "Unable to load service info " + resolveInfo, e);
132e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            }
133e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        }
1341810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill        if (Log.isLoggable(TAG, Log.DEBUG)) {
1351810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill            Log.d(TAG, "Loaded settings: " + settings);
1361810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill        }
137e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
138e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        return settings;
139e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    }
140e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
141e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    /**
142421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill     * Returns the settings parsed from the attributes of the
143421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill     * {@link SettingInjectorService#META_DATA_NAME} tag, or null.
144e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     *
145e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
146e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     */
147e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    private static InjectedSetting parseServiceInfo(ResolveInfo service, PackageManager pm)
148e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            throws XmlPullParserException, IOException {
149e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
150e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        ServiceInfo si = service.serviceInfo;
151421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill        ApplicationInfo ai = si.applicationInfo;
152421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill
153421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill        if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
154421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill            if (Log.isLoggable(TAG, Log.WARN)) {
155421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill                Log.w(TAG, "Ignoring attempt to inject setting from app not in system image: "
156421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill                        + service);
157421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill                return null;
158421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill            }
159421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill        }
160e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
161e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        XmlResourceParser parser = null;
162e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        try {
1631810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill            parser = si.loadXmlMetaData(pm, SettingInjectorService.META_DATA_NAME);
164e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            if (parser == null) {
1651810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                throw new XmlPullParserException("No " + SettingInjectorService.META_DATA_NAME
166e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill                        + " meta-data for " + service + ": " + si);
167e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            }
168e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
169e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            AttributeSet attrs = Xml.asAttributeSet(parser);
170e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
171e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            int type;
172e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
173e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill                    && type != XmlPullParser.START_TAG) {
174e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            }
175e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
176e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            String nodeName = parser.getName();
1771810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill            if (!SettingInjectorService.ATTRIBUTES_NAME.equals(nodeName)) {
178e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill                throw new XmlPullParserException("Meta-data does not start with "
1791810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                        + SettingInjectorService.ATTRIBUTES_NAME + " tag");
180e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            }
181e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
182421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill            Resources res = pm.getResourcesForApplication(ai);
183e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            return parseAttributes(si.packageName, si.name, res, attrs);
184e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        } catch (PackageManager.NameNotFoundException e) {
185e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            throw new XmlPullParserException(
186e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill                    "Unable to load resources for package " + si.packageName);
187e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        } finally {
188e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            if (parser != null) {
189e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill                parser.close();
190e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            }
191e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        }
192e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    }
193e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
194e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    /**
195e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * Returns an immutable representation of the static attributes for the setting, or null.
196e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     */
197e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    private static InjectedSetting parseAttributes(
198e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            String packageName, String className, Resources res, AttributeSet attrs) {
199e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
2001810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill        TypedArray sa = res.obtainAttributes(attrs, android.R.styleable.SettingInjectorService);
201e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        try {
202e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            // Note that to help guard against malicious string injection, we do not allow dynamic
203e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            // specification of the label (setting title)
204421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill            final String title = sa.getString(android.R.styleable.SettingInjectorService_title);
205421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill            final int iconId =
206421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill                    sa.getResourceId(android.R.styleable.SettingInjectorService_icon, 0);
207e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            final String settingsActivity =
2081810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                    sa.getString(android.R.styleable.SettingInjectorService_settingsActivity);
209e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            if (Log.isLoggable(TAG, Log.DEBUG)) {
210421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill                Log.d(TAG, "parsed title: " + title + ", iconId: " + iconId
211e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                        + ", settingsActivity: " + settingsActivity);
212e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            }
213e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            return InjectedSetting.newInstance(packageName, className,
214421dccd5dd8cdec03bbc704b142f3ca2396f7e40Tom O'Neill                    title, iconId, settingsActivity);
215e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        } finally {
216e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            sa.recycle();
217e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        }
218e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    }
219e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
220e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    /**
22125518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang     * Gets a list of preferences that other apps have injected.
222e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     */
22332e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang    public List<Preference> getInjectedSettings() {
22432e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang        Iterable<InjectedSetting> settings = getSettings();
22525518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang        ArrayList<Preference> prefs = new ArrayList<Preference>();
226e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        for (InjectedSetting setting : settings) {
22732e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang            Preference pref = addServiceSetting(prefs, setting);
228e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            mSettings.add(new Setting(setting, pref));
22925518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang        }
230e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
231e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        reloadStatusMessages();
23225518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang
23325518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang        return prefs;
234e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    }
235e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
236e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    /**
23732e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang     * Reloads the status messages for all the preference items.
23832e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang     */
23932e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang    public void reloadStatusMessages() {
240e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        if (Log.isLoggable(TAG, Log.DEBUG)) {
241e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            Log.d(TAG, "reloadingStatusMessages: " + mSettings);
24232e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang        }
243e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        mHandler.sendMessage(mHandler.obtainMessage(WHAT_RELOAD));
24432e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang    }
24532e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang
24632e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang    /**
247e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     * Adds an injected setting to the root with status "Loading...".
248e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     */
24932e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang    private Preference addServiceSetting(List<Preference> prefs, InjectedSetting info) {
25032e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang        Preference pref = new Preference(mContext);
25108a4c33a409f7cc693527de138179f80bf07be9cLifu Tang        pref.setTitle(info.title);
25208a4c33a409f7cc693527de138179f80bf07be9cLifu Tang        pref.setSummary(R.string.location_loading_injected_setting);
25332e016b5fae1f4caf673ff1f062869dce0e94757Lifu Tang        PackageManager pm = mContext.getPackageManager();
254e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        Drawable icon = pm.getDrawable(info.packageName, info.iconId, null);
25508a4c33a409f7cc693527de138179f80bf07be9cLifu Tang        pref.setIcon(icon);
256e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
257e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        Intent settingIntent = new Intent();
258e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        settingIntent.setClassName(info.packageName, info.settingsActivity);
25908a4c33a409f7cc693527de138179f80bf07be9cLifu Tang        pref.setIntent(settingIntent);
260e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
26108a4c33a409f7cc693527de138179f80bf07be9cLifu Tang        prefs.add(pref);
26208a4c33a409f7cc693527de138179f80bf07be9cLifu Tang        return pref;
263e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    }
264e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill
265e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    /**
266e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * Loads the setting status values one at a time. Each load starts a subclass of {@link
267e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * SettingInjectorService}, so to reduce memory pressure we don't want to load too many at
268e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * once.
269e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill     */
270e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    private final class StatusLoadingHandler extends Handler {
271e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
272e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        /**
273f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill         * Settings whose status values need to be loaded. A set is used to prevent redundant loads.
274e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill         */
275f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        private Set<Setting> mSettingsToLoad = new HashSet<Setting>();
276e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
277e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        /**
278f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill         * Settings that are being loaded now and haven't timed out. In practice this should have
279f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill         * zero or one elements.
280e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill         */
281f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        private Set<Setting> mSettingsBeingLoaded = new HashSet<Setting>();
282f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill
283f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        /**
284f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill         * Settings that are being loaded but have timed out. If only one setting has timed out, we
285f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill         * will go ahead and start loading the next setting so that one slow load won't delay the
286f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill         * load of the other settings.
287f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill         */
288f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        private Set<Setting> mTimedOutSettings = new HashSet<Setting>();
289f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill
290f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        private boolean mReloadRequested;
291e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
292e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        @Override
293e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        public void handleMessage(Message msg) {
294e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            if (Log.isLoggable(TAG, Log.DEBUG)) {
295f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                Log.d(TAG, "handleMessage start: " + msg + ", " + this);
296e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            }
297e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
298f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            // Update state in response to message
299e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            switch (msg.what) {
300e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                case WHAT_RELOAD:
301f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    mReloadRequested = true;
302f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    break;
303f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                case WHAT_RECEIVED_STATUS:
304f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    final Setting receivedSetting = (Setting) msg.obj;
3051810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                    receivedSetting.maybeLogElapsedTime();
306f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    mSettingsBeingLoaded.remove(receivedSetting);
307f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    mTimedOutSettings.remove(receivedSetting);
308f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    removeMessages(WHAT_TIMEOUT, receivedSetting);
309e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    break;
310e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                case WHAT_TIMEOUT:
311f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    final Setting timedOutSetting = (Setting) msg.obj;
312f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    mSettingsBeingLoaded.remove(timedOutSetting);
313f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    mTimedOutSettings.add(timedOutSetting);
314e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    if (Log.isLoggable(TAG, Log.WARN)) {
3157f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill                        Log.w(TAG, "Timed out after " + timedOutSetting.getElapsedTime()
3167f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill                                + " millis trying to get status for: " + timedOutSetting);
317e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    }
318e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    break;
319e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                default:
320f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    Log.wtf(TAG, "Unexpected what: " + msg);
321f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            }
322f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill
323007994279c6d9e41fe35a6804eb4ca753d139969Lifu Tang            // Decide whether to load additional settings based on the new state. Start by seeing
324f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            // if we have headroom to load another setting.
325f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            if (mSettingsBeingLoaded.size() > 0 || mTimedOutSettings.size() > 1) {
326f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                // Don't load any more settings until one of the pending settings has completed.
327f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                // To reduce memory pressure, we want to be loading at most one setting (plus at
328f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                // most one timed-out setting) at a time. This means we'll be responsible for
329f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                // bringing in at most two services.
330f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                if (Log.isLoggable(TAG, Log.VERBOSE)) {
331f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    Log.v(TAG, "too many services already live for " + msg + ", " + this);
332f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                }
333f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                return;
334f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            }
335f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill
336f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            if (mReloadRequested && mSettingsToLoad.isEmpty() && mSettingsBeingLoaded.isEmpty()
337f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    && mTimedOutSettings.isEmpty()) {
338f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                if (Log.isLoggable(TAG, Log.VERBOSE)) {
339f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    Log.v(TAG, "reloading because idle and reload requesteed " + msg + ", " + this);
340f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                }
341f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                // Reload requested, so must reload all settings
342f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                mSettingsToLoad.addAll(mSettings);
343f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                mReloadRequested = false;
344e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            }
345e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
346e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            // Remove the next setting to load from the queue, if any
347e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            Iterator<Setting> iter = mSettingsToLoad.iterator();
348e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            if (!iter.hasNext()) {
349f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                if (Log.isLoggable(TAG, Log.VERBOSE)) {
350f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    Log.v(TAG, "nothing left to do for " + msg + ", " + this);
351f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                }
352e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                return;
353e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            }
354e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            Setting setting = iter.next();
355e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            iter.remove();
356e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
357e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            // Request the status value
3581810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill            setting.startService();
359f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            mSettingsBeingLoaded.add(setting);
360e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
361e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            // Ensure that if receiving the status value takes too long, we start loading the
362e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            // next value anyway
363e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            Message timeoutMsg = obtainMessage(WHAT_TIMEOUT, setting);
364e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            sendMessageDelayed(timeoutMsg, INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS);
365e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
366e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            if (Log.isLoggable(TAG, Log.DEBUG)) {
367f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                Log.d(TAG, "handleMessage end " + msg + ", " + this
368f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                        + ", started loading " + setting);
369e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            }
370e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        }
371f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill
372f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        @Override
373f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        public String toString() {
374f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            return "StatusLoadingHandler{" +
375f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    "mSettingsToLoad=" + mSettingsToLoad +
376f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    ", mSettingsBeingLoaded=" + mSettingsBeingLoaded +
377f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    ", mTimedOutSettings=" + mTimedOutSettings +
378f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    ", mReloadRequested=" + mReloadRequested +
379f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill                    '}';
380f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        }
381e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    }
382e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
383e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    /**
384e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     * Represents an injected setting and the corresponding preference.
385e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill     */
386e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill    private final class Setting {
387e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
388e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        public final InjectedSetting setting;
389e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        public final Preference preference;
3901810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill        public long startMillis;
391e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
392e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        private Setting(InjectedSetting setting, Preference preference) {
393e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            this.setting = setting;
394e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            this.preference = preference;
395e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        }
396e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
397e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        @Override
398e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        public String toString() {
399e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            return "Setting{" +
400e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    "setting=" + setting +
401e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    ", preference=" + preference +
402e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    '}';
403e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        }
404e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill
405e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill        /**
406f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill         * Returns true if they both have the same {@link #setting} value. Ignores mutable
4071810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill         * {@link #preference} and {@link #startMillis} so that it's safe to use in sets.
408f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill         */
409f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        @Override
410f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        public boolean equals(Object o) {
411f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            return this == o || o instanceof Setting && setting.equals(((Setting) o).setting);
412f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        }
413f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill
414f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        @Override
415f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        public int hashCode() {
416f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill            return setting.hashCode();
417f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        }
418f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill
419f06dc3216318672d5a30d58c0cfcb4142dbab2c2Tom O'Neill        /**
4201810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill         * Starts the service to fetch for the current status for the setting, and updates the
4211810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill         * preference when the service replies.
422e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill         */
4231810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill        public void startService() {
424e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            Handler handler = new Handler() {
425e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                @Override
426e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                public void handleMessage(Message msg) {
427e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    Bundle bundle = msg.getData();
428b578ac4f94542eb7a2988e51045b931305871b97Tom O'Neill                    String summary = bundle.getString(SettingInjectorService.SUMMARY_KEY);
429e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    boolean enabled = bundle.getBoolean(SettingInjectorService.ENABLED_KEY, true);
430e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    if (Log.isLoggable(TAG, Log.DEBUG)) {
431e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                        Log.d(TAG, setting + ": received " + msg + ", bundle: " + bundle);
432e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    }
433b578ac4f94542eb7a2988e51045b931305871b97Tom O'Neill                    preference.setSummary(summary);
434e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    preference.setEnabled(enabled);
435e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                    mHandler.sendMessage(
436e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                            mHandler.obtainMessage(WHAT_RECEIVED_STATUS, Setting.this));
43725518c3b45827537b2b2c2b35fdc86bc8bd60502Lifu Tang                }
438e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            };
439e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            Messenger messenger = new Messenger(handler);
4401810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill
4411810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill            Intent intent = setting.getServiceIntent();
4421810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill            intent.putExtra(SettingInjectorService.MESSENGER_KEY, messenger);
4431810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill
444e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill            if (Log.isLoggable(TAG, Log.DEBUG)) {
4451810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                Log.d(TAG, setting + ": sending update intent: " + intent
446e17ce5fb73efe8046f9d806a6fbb9a5abb5b0e87Tom O'Neill                        + ", handler: " + handler);
4471810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                startMillis = SystemClock.elapsedRealtime();
4481810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill            } else {
4491810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill                startMillis = 0;
4501810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill            }
4517f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill
4527f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill            // Start the service, making sure that this is attributed to the current user rather
4537f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill            // than the system user.
4547f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill            mContext.startServiceAsUser(intent, android.os.Process.myUserHandle());
4557f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill        }
4567f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill
4577f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill        public long getElapsedTime() {
4587f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill            long end = SystemClock.elapsedRealtime();
4597f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill            return end - startMillis;
4601810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill        }
4611810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill
4621810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill        public void maybeLogElapsedTime() {
4631810a88e5fcb81dfa6ebac4f155e8ca63789d3d4Tom O'Neill            if (Log.isLoggable(TAG, Log.DEBUG) && startMillis != 0) {
4647f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill                long elapsed = getElapsedTime();
4657f91d8628320f8669efb2ba660c7ca1dcd2da881Tom O'Neill                Log.d(TAG, this + " update took " + elapsed + " millis");
466e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill            }
467e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill        }
468e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill    }
469e2921c9566e34fd95b864cca247ff71cf9b30752Tom O'Neill}
470