blob: 40ee5d88fc8ab12f837811a8bd4344b6ed5f140e [file] [log] [blame]
/**
* 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;
}
}
/**
* Return all VrListenerService components installed for this user.
*
* @param userId ID of the user to check.
* @return a set of {@link ComponentName}s.
*/
public ArraySet<ComponentName> getInstalled(int userId) {
synchronized (mLock) {
return mInstalledSet.get(userId);
}
}
/**
* Return all VrListenerService components enabled for this user.
*
* @param userId ID of the user to check.
* @return a set of {@link ComponentName}s.
*/
public ArraySet<ComponentName> getEnabled(int userId) {
synchronized (mLock) {
return mEnabledSet.get(userId);
}
}
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 |
PackageManager.MATCH_DIRECT_BOOT_AWARE |
PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
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();
}
}
}