EnabledComponentsObserver.java revision 2ff9c01975622ce7b0e1f7679f1337004dca7be9
1/**
2 * Copyright (C) 2016 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 */
16package com.android.server.vr;
17
18import android.annotation.NonNull;
19import android.app.ActivityManager;
20import android.content.ComponentName;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.content.pm.ServiceInfo;
27import android.os.Handler;
28import android.os.Looper;
29import android.os.UserHandle;
30import android.os.UserManager;
31import android.provider.Settings;
32import android.text.TextUtils;
33import android.util.ArraySet;
34import android.util.Slog;
35import android.util.SparseArray;
36
37import com.android.internal.content.PackageMonitor;
38import com.android.server.vr.SettingsObserver.SettingChangeListener;
39
40import java.util.Collection;
41import java.util.List;
42import java.util.Set;
43
44/**
45 * Detects changes in packages, settings, and current users that may affect whether components
46 * implementing a given service can be run.
47 *
48 * @hide
49 */
50public class EnabledComponentsObserver implements SettingChangeListener {
51
52    private static final String TAG = EnabledComponentsObserver.class.getSimpleName();
53    private static final String ENABLED_SERVICES_SEPARATOR = ":";
54
55    public static final int NO_ERROR = 0;
56    public static final int DISABLED = -1;
57    public static final int NOT_INSTALLED = -2;
58
59    private final Object mLock;
60    private final Context mContext;
61    private final String mSettingName;
62    private final String mServiceName;
63    private final String mServicePermission;
64    private final SparseArray<ArraySet<ComponentName>> mInstalledSet = new SparseArray<>();
65    private final SparseArray<ArraySet<ComponentName>> mEnabledSet = new SparseArray<>();
66    private final Set<EnabledComponentChangeListener> mEnabledComponentListeners = new ArraySet<>();
67
68    /**
69     * Implement this to receive callbacks when relevant changes to the allowed components occur.
70     */
71    public interface EnabledComponentChangeListener {
72
73        /**
74         * Called when a change in the allowed components occurs.
75         */
76        void onEnabledComponentChanged();
77    }
78
79    private EnabledComponentsObserver(@NonNull Context context, @NonNull String settingName,
80            @NonNull String servicePermission, @NonNull String serviceName, @NonNull Object lock,
81            @NonNull Collection<EnabledComponentChangeListener> listeners) {
82        mLock = lock;
83        mContext = context;
84        mSettingName = settingName;
85        mServiceName = serviceName;
86        mServicePermission = servicePermission;
87        mEnabledComponentListeners.addAll(listeners);
88    }
89
90    /**
91     * Create a EnabledComponentObserver instance.
92     *
93     * @param context the context to query for changes.
94     * @param handler a handler to receive lifecycle events from system services on.
95     * @param settingName the name of a setting to monitor for a list of enabled components.
96     * @param looper a {@link Looper} to use for receiving package callbacks.
97     * @param servicePermission the permission required by the components to be bound.
98     * @param serviceName the intent action implemented by the tracked components.
99     * @param lock a lock object used to guard instance state in all callbacks and method calls.
100     * @return an EnableComponentObserver instance.
101     */
102    public static EnabledComponentsObserver build(@NonNull Context context,
103            @NonNull Handler handler, @NonNull String settingName, @NonNull Looper looper,
104            @NonNull String servicePermission, @NonNull String serviceName,
105            @NonNull final Object lock,
106            @NonNull Collection<EnabledComponentChangeListener> listeners) {
107
108        SettingsObserver s = SettingsObserver.build(context, handler, settingName);
109
110        final EnabledComponentsObserver o = new EnabledComponentsObserver(context, settingName,
111                servicePermission, serviceName, lock, listeners);
112
113        PackageMonitor packageMonitor = new PackageMonitor() {
114            @Override
115            public void onSomePackagesChanged() {
116                o.onPackagesChanged();
117
118            }
119
120            @Override
121            public void onPackageDisappeared(String packageName, int reason) {
122                o.onPackagesChanged();
123
124            }
125
126            @Override
127            public void onPackageModified(String packageName) {
128                o.onPackagesChanged();
129
130            }
131
132            @Override
133            public boolean onHandleForceStop(Intent intent, String[] packages, int uid,
134                    boolean doit) {
135                o.onPackagesChanged();
136
137                return super.onHandleForceStop(intent, packages, uid, doit);
138            }
139        };
140
141        packageMonitor.register(context, looper, UserHandle.ALL, true);
142
143        s.addListener(o);
144
145        return o;
146
147    }
148
149    public void onPackagesChanged() {
150        rebuildAll();
151    }
152
153    @Override
154    public void onSettingChanged() {
155        rebuildAll();
156    }
157
158    @Override
159    public void onSettingRestored(String prevValue, String newValue, int userId) {
160        rebuildAll();
161    }
162
163    public void onUsersChanged() {
164        rebuildAll();
165    }
166
167    /**
168     * Rebuild the sets of allowed components for each current user profile.
169     */
170    public void rebuildAll() {
171        synchronized (mLock) {
172            mInstalledSet.clear();
173            mEnabledSet.clear();
174            final int[] userIds = getCurrentProfileIds();
175            for (int i : userIds) {
176                ArraySet<ComponentName> implementingPackages = loadComponentNamesForUser(i);
177                ArraySet<ComponentName> packagesFromSettings =
178                        loadComponentNamesFromSetting(mSettingName, i);
179                packagesFromSettings.retainAll(implementingPackages);
180
181                mInstalledSet.put(i, implementingPackages);
182                mEnabledSet.put(i, packagesFromSettings);
183
184            }
185        }
186        sendSettingChanged();
187    }
188
189    /**
190     * Check whether a given component is present and enabled for the given user.
191     *
192     * @param component the component to check.
193     * @param userId the user ID for the component to check.
194     * @return {@code true} if present and enabled.
195     */
196    public int isValid(ComponentName component, int userId) {
197        synchronized (mLock) {
198            ArraySet<ComponentName> installedComponents = mInstalledSet.get(userId);
199            if (installedComponents == null || !installedComponents.contains(component)) {
200                return NOT_INSTALLED;
201            }
202            ArraySet<ComponentName> validComponents = mEnabledSet.get(userId);
203            if (validComponents == null || !validComponents.contains(component)) {
204                return DISABLED;
205            }
206            return NO_ERROR;
207        }
208    }
209
210    /**
211     * Return all VrListenerService components installed for this user.
212     *
213     * @param userId ID of the user to check.
214     * @return a set of {@link ComponentName}s.
215     */
216    public ArraySet<ComponentName> getInstalled(int userId) {
217        synchronized (mLock) {
218            return mInstalledSet.get(userId);
219        }
220    }
221
222    /**
223     * Return all VrListenerService components enabled for this user.
224     *
225     * @param userId ID of the user to check.
226     * @return a set of {@link ComponentName}s.
227     */
228    public ArraySet<ComponentName> getEnabled(int userId) {
229        synchronized (mLock) {
230            return mEnabledSet.get(userId);
231        }
232    }
233
234    private int[] getCurrentProfileIds() {
235        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
236        if (userManager == null) {
237            return null;
238        }
239        return userManager.getEnabledProfileIds(ActivityManager.getCurrentUser());
240    }
241
242    public static ArraySet<ComponentName> loadComponentNames(PackageManager pm, int userId,
243            String serviceName, String permissionName) {
244
245        ArraySet<ComponentName> installed = new ArraySet<>();
246        Intent queryIntent = new Intent(serviceName);
247        List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
248                queryIntent,
249                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
250                userId);
251        if (installedServices != null) {
252            for (int i = 0, count = installedServices.size(); i < count; i++) {
253                ResolveInfo resolveInfo = installedServices.get(i);
254                ServiceInfo info = resolveInfo.serviceInfo;
255
256                ComponentName component = new ComponentName(info.packageName, info.name);
257                if (!permissionName.equals(info.permission)) {
258                    Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name
259                            + ": it does not require the permission "
260                            + permissionName);
261                    continue;
262                }
263                installed.add(component);
264            }
265        }
266        return installed;
267    }
268
269    private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
270        return loadComponentNames(mContext.getPackageManager(), userId, mServiceName,
271                mServicePermission);
272    }
273
274    private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
275            int userId) {
276        final ContentResolver cr = mContext.getContentResolver();
277        String settingValue = Settings.Secure.getStringForUser(
278                cr,
279                settingName,
280                userId);
281        if (TextUtils.isEmpty(settingValue))
282            return new ArraySet<>();
283        String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR);
284        ArraySet<ComponentName> result = new ArraySet<>(restored.length);
285        for (int i = 0; i < restored.length; i++) {
286            ComponentName value = ComponentName.unflattenFromString(restored[i]);
287            if (null != value) {
288                result.add(value);
289            }
290        }
291        return result;
292    }
293
294    private void sendSettingChanged() {
295        for (EnabledComponentChangeListener l : mEnabledComponentListeners) {
296            l.onEnabledComponentChanged();
297        }
298    }
299
300}
301