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            ArraySet<ComponentName> ret = mInstalledSet.get(userId);
219            if (ret == null) {
220                return new ArraySet<ComponentName>();
221            }
222            return ret;
223        }
224    }
225
226    /**
227     * Return all VrListenerService components enabled for this user.
228     *
229     * @param userId ID of the user to check.
230     * @return a set of {@link ComponentName}s.
231     */
232    public ArraySet<ComponentName> getEnabled(int userId) {
233        synchronized (mLock) {
234            ArraySet<ComponentName> ret = mEnabledSet.get(userId);
235            if (ret == null) {
236                return new ArraySet<ComponentName>();
237            }
238            return ret;
239
240        }
241    }
242
243    private int[] getCurrentProfileIds() {
244        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
245        if (userManager == null) {
246            return null;
247        }
248        return userManager.getEnabledProfileIds(ActivityManager.getCurrentUser());
249    }
250
251    public static ArraySet<ComponentName> loadComponentNames(PackageManager pm, int userId,
252            String serviceName, String permissionName) {
253
254        ArraySet<ComponentName> installed = new ArraySet<>();
255        Intent queryIntent = new Intent(serviceName);
256        List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
257                queryIntent,
258                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA |
259                                    PackageManager.MATCH_DIRECT_BOOT_AWARE |
260                                    PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
261                userId);
262        if (installedServices != null) {
263            for (int i = 0, count = installedServices.size(); i < count; i++) {
264                ResolveInfo resolveInfo = installedServices.get(i);
265                ServiceInfo info = resolveInfo.serviceInfo;
266
267                ComponentName component = new ComponentName(info.packageName, info.name);
268                if (!permissionName.equals(info.permission)) {
269                    Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name
270                            + ": it does not require the permission "
271                            + permissionName);
272                    continue;
273                }
274                installed.add(component);
275            }
276        }
277        return installed;
278    }
279
280    private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
281        return loadComponentNames(mContext.getPackageManager(), userId, mServiceName,
282                mServicePermission);
283    }
284
285    private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
286            int userId) {
287        final ContentResolver cr = mContext.getContentResolver();
288        String settingValue = Settings.Secure.getStringForUser(
289                cr,
290                settingName,
291                userId);
292        if (TextUtils.isEmpty(settingValue))
293            return new ArraySet<>();
294        String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR);
295        ArraySet<ComponentName> result = new ArraySet<>(restored.length);
296        for (int i = 0; i < restored.length; i++) {
297            ComponentName value = ComponentName.unflattenFromString(restored[i]);
298            if (null != value) {
299                result.add(value);
300            }
301        }
302        return result;
303    }
304
305    private void sendSettingChanged() {
306        for (EnabledComponentChangeListener l : mEnabledComponentListeners) {
307            l.onEnabledComponentChanged();
308        }
309    }
310
311}
312