| /** |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.server.vr; |
| |
| import android.annotation.NonNull; |
| import android.app.ActivityManager; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.content.pm.ServiceInfo; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.provider.Settings; |
| import android.text.TextUtils; |
| import android.util.ArraySet; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| |
| import com.android.internal.content.PackageMonitor; |
| import com.android.server.vr.SettingsObserver.SettingChangeListener; |
| |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * Detects changes in packages, settings, and current users that may affect whether components |
| * implementing a given service can be run. |
| * |
| * @hide |
| */ |
| public class EnabledComponentsObserver implements SettingChangeListener { |
| |
| private static final String TAG = EnabledComponentsObserver.class.getSimpleName(); |
| private static final String ENABLED_SERVICES_SEPARATOR = ":"; |
| |
| public static final int NO_ERROR = 0; |
| public static final int DISABLED = -1; |
| public static final int NOT_INSTALLED = -2; |
| |
| private final Object mLock; |
| private final Context mContext; |
| private final String mSettingName; |
| private final String mServiceName; |
| private final String mServicePermission; |
| private final SparseArray<ArraySet<ComponentName>> mInstalledSet = new SparseArray<>(); |
| private final SparseArray<ArraySet<ComponentName>> mEnabledSet = new SparseArray<>(); |
| private final Set<EnabledComponentChangeListener> mEnabledComponentListeners = new ArraySet<>(); |
| |
| /** |
| * Implement this to receive callbacks when relevant changes to the allowed components occur. |
| */ |
| public interface EnabledComponentChangeListener { |
| |
| /** |
| * Called when a change in the allowed components occurs. |
| */ |
| void onEnabledComponentChanged(); |
| } |
| |
| private EnabledComponentsObserver(@NonNull Context context, @NonNull String settingName, |
| @NonNull String servicePermission, @NonNull String serviceName, @NonNull Object lock, |
| @NonNull Collection<EnabledComponentChangeListener> listeners) { |
| mLock = lock; |
| mContext = context; |
| mSettingName = settingName; |
| mServiceName = serviceName; |
| mServicePermission = servicePermission; |
| mEnabledComponentListeners.addAll(listeners); |
| } |
| |
| /** |
| * Create a EnabledComponentObserver instance. |
| * |
| * @param context the context to query for changes. |
| * @param handler a handler to receive lifecycle events from system services on. |
| * @param settingName the name of a setting to monitor for a list of enabled components. |
| * @param looper a {@link Looper} to use for receiving package callbacks. |
| * @param servicePermission the permission required by the components to be bound. |
| * @param serviceName the intent action implemented by the tracked components. |
| * @param lock a lock object used to guard instance state in all callbacks and method calls. |
| * @return an EnableComponentObserver instance. |
| */ |
| public static EnabledComponentsObserver build(@NonNull 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) { |
| |
| SettingsObserver s = SettingsObserver.build(context, handler, settingName); |
| |
| final EnabledComponentsObserver o = new EnabledComponentsObserver(context, settingName, |
| servicePermission, serviceName, lock, listeners); |
| |
| PackageMonitor packageMonitor = new PackageMonitor() { |
| @Override |
| public void onSomePackagesChanged() { |
| o.onPackagesChanged(); |
| |
| } |
| |
| @Override |
| public void onPackageDisappeared(String packageName, int reason) { |
| o.onPackagesChanged(); |
| |
| } |
| |
| @Override |
| public void onPackageModified(String packageName) { |
| o.onPackagesChanged(); |
| |
| } |
| |
| @Override |
| public boolean onHandleForceStop(Intent intent, String[] packages, int uid, |
| boolean doit) { |
| o.onPackagesChanged(); |
| |
| return super.onHandleForceStop(intent, packages, uid, doit); |
| } |
| }; |
| |
| packageMonitor.register(context, looper, UserHandle.ALL, true); |
| |
| s.addListener(o); |
| |
| return o; |
| |
| } |
| |
| public void onPackagesChanged() { |
| rebuildAll(); |
| } |
| |
| @Override |
| public void onSettingChanged() { |
| rebuildAll(); |
| } |
| |
| @Override |
| public void onSettingRestored(String prevValue, String newValue, int userId) { |
| rebuildAll(); |
| } |
| |
| public void onUsersChanged() { |
| rebuildAll(); |
| } |
| |
| /** |
| * Rebuild the sets of allowed components for each current user profile. |
| */ |
| public void rebuildAll() { |
| synchronized (mLock) { |
| mInstalledSet.clear(); |
| mEnabledSet.clear(); |
| final int[] userIds = getCurrentProfileIds(); |
| for (int i : userIds) { |
| ArraySet<ComponentName> implementingPackages = loadComponentNamesForUser(i); |
| ArraySet<ComponentName> packagesFromSettings = |
| loadComponentNamesFromSetting(mSettingName, i); |
| packagesFromSettings.retainAll(implementingPackages); |
| |
| mInstalledSet.put(i, implementingPackages); |
| mEnabledSet.put(i, packagesFromSettings); |
| |
| } |
| } |
| sendSettingChanged(); |
| } |
| |
| /** |
| * Check whether a given component is present and enabled for the given user. |
| * |
| * @param component the component to check. |
| * @param userId the user ID for the component to check. |
| * @return {@code true} if present and enabled. |
| */ |
| public int isValid(ComponentName component, int userId) { |
| synchronized (mLock) { |
| ArraySet<ComponentName> installedComponents = mInstalledSet.get(userId); |
| if (installedComponents == null || !installedComponents.contains(component)) { |
| return NOT_INSTALLED; |
| } |
| ArraySet<ComponentName> validComponents = mEnabledSet.get(userId); |
| if (validComponents == null || !validComponents.contains(component)) { |
| return DISABLED; |
| } |
| return NO_ERROR; |
| } |
| } |
| |
| private int[] getCurrentProfileIds() { |
| UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); |
| if (userManager == null) { |
| return null; |
| } |
| return userManager.getEnabledProfileIds(ActivityManager.getCurrentUser()); |
| } |
| |
| public static ArraySet<ComponentName> loadComponentNames(PackageManager pm, int userId, |
| String serviceName, String permissionName) { |
| |
| ArraySet<ComponentName> installed = new ArraySet<>(); |
| Intent queryIntent = new Intent(serviceName); |
| List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser( |
| queryIntent, |
| PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, |
| userId); |
| if (installedServices != null) { |
| for (int i = 0, count = installedServices.size(); i < count; i++) { |
| ResolveInfo resolveInfo = installedServices.get(i); |
| ServiceInfo info = resolveInfo.serviceInfo; |
| |
| ComponentName component = new ComponentName(info.packageName, info.name); |
| if (!permissionName.equals(info.permission)) { |
| Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name |
| + ": it does not require the permission " |
| + permissionName); |
| continue; |
| } |
| installed.add(component); |
| } |
| } |
| return installed; |
| } |
| |
| private ArraySet<ComponentName> loadComponentNamesForUser(int userId) { |
| return loadComponentNames(mContext.getPackageManager(), userId, mServiceName, |
| mServicePermission); |
| } |
| |
| private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName, |
| int userId) { |
| final ContentResolver cr = mContext.getContentResolver(); |
| String settingValue = Settings.Secure.getStringForUser( |
| cr, |
| settingName, |
| userId); |
| if (TextUtils.isEmpty(settingValue)) |
| return new ArraySet<>(); |
| String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR); |
| ArraySet<ComponentName> result = new ArraySet<>(restored.length); |
| for (int i = 0; i < restored.length; i++) { |
| ComponentName value = ComponentName.unflattenFromString(restored[i]); |
| if (null != value) { |
| result.add(value); |
| } |
| } |
| return result; |
| } |
| |
| private void sendSettingChanged() { |
| for (EnabledComponentChangeListener l : mEnabledComponentListeners) { |
| l.onEnabledComponentChanged(); |
| } |
| } |
| |
| } |