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