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  */
16 package com.android.server.vr;
17 
18 import android.annotation.NonNull;
19 import android.app.ActivityManager;
20 import android.content.ComponentName;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.content.pm.ServiceInfo;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.os.UserHandle;
30 import android.os.UserManager;
31 import android.provider.Settings;
32 import android.text.TextUtils;
33 import android.util.ArraySet;
34 import android.util.Slog;
35 import android.util.SparseArray;
36 
37 import com.android.internal.content.PackageMonitor;
38 import com.android.server.vr.SettingsObserver.SettingChangeListener;
39 
40 import java.util.Collection;
41 import java.util.List;
42 import 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  */
50 public 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          */
onEnabledComponentChanged()76         void onEnabledComponentChanged();
77     }
78 
EnabledComponentsObserver(@onNull Context context, @NonNull String settingName, @NonNull String servicePermission, @NonNull String serviceName, @NonNull Object lock, @NonNull Collection<EnabledComponentChangeListener> listeners)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      */
build(@onNull Context context, @NonNull Handler handler, @NonNull String settingName, @NonNull Looper looper, @NonNull String servicePermission, @NonNull String serviceName, @NonNull final Object lock, @NonNull Collection<EnabledComponentChangeListener> listeners)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 
onPackagesChanged()149     public void onPackagesChanged() {
150         rebuildAll();
151     }
152 
153     @Override
onSettingChanged()154     public void onSettingChanged() {
155         rebuildAll();
156     }
157 
158     @Override
onSettingRestored(String prevValue, String newValue, int userId)159     public void onSettingRestored(String prevValue, String newValue, int userId) {
160         rebuildAll();
161     }
162 
onUsersChanged()163     public void onUsersChanged() {
164         rebuildAll();
165     }
166 
167     /**
168      * Rebuild the sets of allowed components for each current user profile.
169      */
rebuildAll()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      */
isValid(ComponentName component, int userId)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      */
getInstalled(int userId)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      */
getEnabled(int userId)228     public ArraySet<ComponentName> getEnabled(int userId) {
229         synchronized (mLock) {
230             return mEnabledSet.get(userId);
231         }
232     }
233 
getCurrentProfileIds()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 
loadComponentNames(PackageManager pm, int userId, String serviceName, String permissionName)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 
loadComponentNamesForUser(int userId)269     private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
270         return loadComponentNames(mContext.getPackageManager(), userId, mServiceName,
271                 mServicePermission);
272     }
273 
loadComponentNamesFromSetting(String settingName, int userId)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 
sendSettingChanged()294     private void sendSettingChanged() {
295         for (EnabledComponentChangeListener l : mEnabledComponentListeners) {
296             l.onEnabledComponentChanged();
297         }
298     }
299 
300 }
301