/**
|
* 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) {
|
ArraySet<ComponentName> ret = mInstalledSet.get(userId);
|
if (ret == null) {
|
return new ArraySet<ComponentName>();
|
}
|
return ret;
|
}
|
}
|
|
/**
|
* 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) {
|
ArraySet<ComponentName> ret = mEnabledSet.get(userId);
|
if (ret == null) {
|
return new ArraySet<ComponentName>();
|
}
|
return ret;
|
|
}
|
}
|
|
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();
|
}
|
}
|
|
}
|