Merge "Grant default permissions to preinstalled VrListenerServices." into nyc-dev

am: 8f7e381

* commit '8f7e3815e71cfb9a45b8632e6b9f37684699ff82':
  Grant default permissions to preinstalled VrListenerServices.

Change-Id: I5332d425146fcac44b662843fa9b10644a6f8e1e
This commit is contained in:
Ruben Brunk
2016-03-31 22:02:55 +00:00
committed by android-build-merger
4 changed files with 331 additions and 50 deletions

View File

@ -19,6 +19,7 @@ package com.android.server;
import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.ArrayUtils.appendInt;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.FeatureInfo; import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Environment; import android.os.Environment;
@ -115,6 +116,9 @@ public class SystemConfig {
// These are the packages that should not run under system user // These are the packages that should not run under system user
final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>(); final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
// These are the components that are enabled by default as VR mode listener services.
final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
public static SystemConfig getInstance() { public static SystemConfig getInstance() {
synchronized (SystemConfig.class) { synchronized (SystemConfig.class) {
if (sInstance == null) { if (sInstance == null) {
@ -168,6 +172,10 @@ public class SystemConfig {
return mSystemUserBlacklistedApps; return mSystemUserBlacklistedApps;
} }
public ArraySet<ComponentName> getDefaultVrComponents() {
return mDefaultVrComponents;
}
SystemConfig() { SystemConfig() {
// Read configuration from system // Read configuration from system
readPermissions(Environment.buildPath( readPermissions(Environment.buildPath(
@ -431,6 +439,19 @@ public class SystemConfig {
mSystemUserBlacklistedApps.add(pkgname); mSystemUserBlacklistedApps.add(pkgname);
} }
XmlUtils.skipCurrentTag(parser); XmlUtils.skipCurrentTag(parser);
} else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
String clsname = parser.getAttributeValue(null, "class");
if (pkgname == null) {
Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile
+ " at " + parser.getPositionDescription());
} else if (clsname == null) {
Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile
+ " at " + parser.getPositionDescription());
} else {
mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
}
XmlUtils.skipCurrentTag(parser);
} else { } else {
XmlUtils.skipCurrentTag(parser); XmlUtils.skipCurrentTag(parser);
continue; continue;

View File

@ -21,6 +21,7 @@ import android.app.ActivityManager;
import android.app.AppGlobals; import android.app.AppGlobals;
import android.app.backup.BackupManager; import android.app.backup.BackupManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
@ -63,6 +64,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor; import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread; import com.android.internal.os.BackgroundThread;
import com.android.providers.settings.SettingsState.Setting; import com.android.providers.settings.SettingsState.Setting;
import com.android.server.SystemConfig;
import java.io.File; import java.io.File;
import java.io.FileDescriptor; import java.io.FileDescriptor;
@ -1940,7 +1942,7 @@ public class SettingsProvider extends ContentProvider {
} }
private final class UpgradeController { private final class UpgradeController {
private static final int SETTINGS_VERSION = 125; private static final int SETTINGS_VERSION = 126;
private final int mUserId; private final int mUserId;
@ -2136,6 +2138,35 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 125; currentVersion = 125;
} }
if (currentVersion == 125) {
// Version 125: Allow OEMs to set the default VR service.
final SettingsState secureSettings = getSecureSettingsLocked(userId);
Setting currentSetting = secureSettings.getSettingLocked(
Settings.Secure.ENABLED_VR_LISTENERS);
if (currentSetting == null) {
ArraySet<ComponentName> l =
SystemConfig.getInstance().getDefaultVrComponents();
if (l != null && !l.isEmpty()) {
StringBuilder b = new StringBuilder();
boolean start = true;
for (ComponentName c : l) {
if (!start) {
b.append(':');
}
b.append(c.flattenToString());
start = false;
}
secureSettings.insertSettingLocked(
Settings.Secure.ENABLED_VR_LISTENERS, b.toString(),
SettingsState.SYSTEM_PACKAGE_NAME);
}
}
currentVersion = 126;
}
// vXXX: Add new settings above this point. // vXXX: Add new settings above this point.
// Return the current version. // Return the current version.

View File

@ -227,10 +227,11 @@ public class EnabledComponentsObserver implements SettingChangeListener {
return userIds; return userIds;
} }
private ArraySet<ComponentName> loadComponentNamesForUser(int userId) { public static ArraySet<ComponentName> loadComponentNames(PackageManager pm, int userId,
String serviceName, String permissionName) {
ArraySet<ComponentName> installed = new ArraySet<>(); ArraySet<ComponentName> installed = new ArraySet<>();
PackageManager pm = mContext.getPackageManager(); Intent queryIntent = new Intent(serviceName);
Intent queryIntent = new Intent(mServiceName);
List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser( List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
queryIntent, queryIntent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
@ -241,10 +242,10 @@ public class EnabledComponentsObserver implements SettingChangeListener {
ServiceInfo info = resolveInfo.serviceInfo; ServiceInfo info = resolveInfo.serviceInfo;
ComponentName component = new ComponentName(info.packageName, info.name); ComponentName component = new ComponentName(info.packageName, info.name);
if (!mServicePermission.equals(info.permission)) { if (!permissionName.equals(info.permission)) {
Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name
+ ": it does not require the permission " + ": it does not require the permission "
+ mServicePermission); + permissionName);
continue; continue;
} }
installed.add(component); installed.add(component);
@ -253,6 +254,11 @@ public class EnabledComponentsObserver implements SettingChangeListener {
return installed; return installed;
} }
private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
return loadComponentNames(mContext.getPackageManager(), userId, mServiceName,
mServicePermission);
}
private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName, private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
int userId) { int userId) {
final ContentResolver cr = mContext.getContentResolver(); final ContentResolver cr = mContext.getContentResolver();

View File

@ -16,16 +16,23 @@
package com.android.server.vr; package com.android.server.vr;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.content.Context;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder; import android.os.Binder;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.IInterface; import android.os.IInterface;
import android.os.Looper; import android.os.Looper;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.vr.IVrListener; import android.service.vr.IVrListener;
import android.service.vr.VrListenerService; import android.service.vr.VrListenerService;
import android.util.ArraySet; import android.util.ArraySet;
@ -38,7 +45,9 @@ import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeLis
import com.android.server.utils.ManagedApplicationService; import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker; import com.android.server.utils.ManagedApplicationService.BinderChecker;
import java.lang.StringBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -53,8 +62,8 @@ import java.util.Set;
* hardware/libhardware/modules/vr * hardware/libhardware/modules/vr
* <p/> * <p/>
* In general applications may enable or disable VR mode by calling * In general applications may enable or disable VR mode by calling
* {@link android.app.Activity#setVrMode)}. An application may also implement a service to be run * {@link android.app.Activity#setVrModeEnabled)}. An application may also implement a service to
* while in VR mode by implementing {@link android.service.vr.VrListenerService}. * be run while in VR mode by implementing {@link android.service.vr.VrListenerService}.
* *
* @see {@link android.service.vr.VrListenerService} * @see {@link android.service.vr.VrListenerService}
* @see {@link com.android.server.vr.VrManagerInternal} * @see {@link com.android.server.vr.VrManagerInternal}
@ -74,13 +83,18 @@ public class VrManagerService extends SystemService implements EnabledComponentC
private final IBinder mOverlayToken = new Binder(); private final IBinder mOverlayToken = new Binder();
// State protected by mLock // State protected by mLock
private boolean mVrModeEnabled = false; private boolean mVrModeEnabled;
private final Set<VrStateListener> mListeners = new ArraySet<>(); private final Set<VrStateListener> mListeners = new ArraySet<>();
private EnabledComponentsObserver mComponentObserver; private EnabledComponentsObserver mComponentObserver;
private ManagedApplicationService mCurrentVrService; private ManagedApplicationService mCurrentVrService;
private Context mContext; private Context mContext;
private ComponentName mCurrentVrModeComponent; private ComponentName mCurrentVrModeComponent;
private int mCurrentVrModeUser; private int mCurrentVrModeUser;
private boolean mWasDefaultGranted;
private boolean mGuard;
private final ArraySet<String> mPreviousToggledListenerSettings = new ArraySet<>();
private String mPreviousNotificationPolicyAccessPackage;
private String mPreviousManageOverlayPackage;
private static final BinderChecker sBinderChecker = new BinderChecker() { private static final BinderChecker sBinderChecker = new BinderChecker() {
@Override @Override
@ -239,62 +253,271 @@ public class VrManagerService extends SystemService implements EnabledComponentC
* *
* @return {@code true} if the component/user combination specified is valid. * @return {@code true} if the component/user combination specified is valid.
*/ */
private boolean updateCurrentVrServiceLocked(boolean enabled, private boolean updateCurrentVrServiceLocked(boolean enabled, @NonNull ComponentName component,
@NonNull ComponentName component, int userId, ComponentName calling) { int userId, ComponentName calling) {
boolean sendUpdatedCaller = false; boolean sendUpdatedCaller = false;
final long identity = Binder.clearCallingIdentity();
try {
boolean validUserComponent = (mComponentObserver.isValid(component, userId) == boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
EnabledComponentsObserver.NO_ERROR); EnabledComponentsObserver.NO_ERROR);
// Always send mode change events. // Always send mode change events.
changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null); changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null);
if (!enabled || !validUserComponent) { if (!enabled || !validUserComponent) {
// Unbind whatever is running // Unbind whatever is running
if (mCurrentVrService != null) { if (mCurrentVrService != null) {
Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
mCurrentVrService.getUserId());
mCurrentVrService.disconnect();
mCurrentVrService = null;
}
} else {
if (mCurrentVrService != null) {
// Unbind any running service that doesn't match the component/user selection
if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " + Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
mCurrentVrService.getUserId()); mCurrentVrService.getUserId());
mCurrentVrService.disconnect();
disableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
new UserHandle(mCurrentVrService.getUserId()));
mCurrentVrService = null;
}
} else {
if (mCurrentVrService != null) {
// Unbind any running service that doesn't match the component/user selection
if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() +
" for user " + mCurrentVrService.getUserId());
disableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
new UserHandle(mCurrentVrService.getUserId()));
createAndConnectService(component, userId);
enableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
new UserHandle(mCurrentVrService.getUserId()));
sendUpdatedCaller = true;
}
// The service with the correct component/user is bound
} else {
// Nothing was previously running, bind a new service
createAndConnectService(component, userId); createAndConnectService(component, userId);
enableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
new UserHandle(mCurrentVrService.getUserId()));
sendUpdatedCaller = true; sendUpdatedCaller = true;
} }
// The service with the correct component/user is bound }
} else {
// Nothing was previously running, bind a new service if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) {
createAndConnectService(component, userId); mCurrentVrModeComponent = calling;
mCurrentVrModeUser = userId;
sendUpdatedCaller = true; sendUpdatedCaller = true;
} }
if (mCurrentVrService != null && sendUpdatedCaller) {
final ComponentName c = mCurrentVrModeComponent;
mCurrentVrService.sendEvent(new PendingEvent() {
@Override
public void runEvent(IInterface service) throws RemoteException {
IVrListener l = (IVrListener) service;
l.focusedActivityChanged(c);
}
});
}
return validUserComponent;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* Enable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given
* component package and user.
*
* @param component the component whose package should be enabled.
* @param userId the user that owns the given component.
*/
private void enableImpliedPermissionsLocked(ComponentName component, UserHandle userId) {
if (mGuard) {
// Impossible
throw new IllegalStateException("Enabling permissions without disabling.");
}
mGuard = true;
PackageManager pm = mContext.getPackageManager();
String pName = component.getPackageName();
if (pm == null) {
Slog.e(TAG, "Couldn't set implied permissions for " + pName +
", PackageManager isn't running");
return;
}
ApplicationInfo info = null;
try {
info = pm.getApplicationInfo(pName, PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
}
if (info == null) {
Slog.e(TAG, "Couldn't set implied permissions for " + pName + ", no such package.");
return;
}
if (!(info.isSystemApp() || info.isUpdatedSystemApp())) {
return; // Application is not pre-installed, avoid setting implied permissions
}
mWasDefaultGranted = true;
grantOverlayAccess(pName, userId);
grantNotificationPolicyAccess(pName);
grantNotificationListenerAccess(pName, userId);
}
/**
* Disable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given
* component package and user.
*
* @param component the component whose package should be disabled.
* @param userId the user that owns the given component.
*/
private void disableImpliedPermissionsLocked(ComponentName component, UserHandle userId) {
if (!mGuard) {
// Impossible
throw new IllegalStateException("Disabling permissions without enabling.");
}
mGuard = false;
PackageManager pm = mContext.getPackageManager();
if (pm == null) {
Slog.e(TAG, "Couldn't remove implied permissions for " + component +
", PackageManager isn't running");
return;
}
String pName = component.getPackageName();
if (mWasDefaultGranted) {
revokeOverlayAccess(userId);
revokeNotificationPolicyAccess(pName);
revokeNotificiationListenerAccess();
mWasDefaultGranted = false;
}
}
private void grantOverlayAccess(String pkg, UserHandle userId) {
PackageManager pm = mContext.getPackageManager();
boolean prev = (PackageManager.PERMISSION_GRANTED ==
pm.checkPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, pkg));
mPreviousManageOverlayPackage = null;
if (!prev) {
pm.grantRuntimePermission(pkg, android.Manifest.permission.SYSTEM_ALERT_WINDOW,
userId);
mPreviousManageOverlayPackage = pkg;
}
}
private void revokeOverlayAccess(UserHandle userId) {
PackageManager pm = mContext.getPackageManager();
if (mPreviousManageOverlayPackage != null) {
pm.revokeRuntimePermission(mPreviousManageOverlayPackage,
android.Manifest.permission.SYSTEM_ALERT_WINDOW, userId);
mPreviousManageOverlayPackage = null;
}
}
private void grantNotificationPolicyAccess(String pkg) {
NotificationManager nm = mContext.getSystemService(NotificationManager.class);
boolean prev = nm.isNotificationPolicyAccessGrantedForPackage(pkg);
mPreviousNotificationPolicyAccessPackage = null;
if (!prev) {
mPreviousNotificationPolicyAccessPackage = pkg;
nm.setNotificationPolicyAccessGranted(pkg, true);
}
}
private void revokeNotificationPolicyAccess(String pkg) {
NotificationManager nm = mContext.getSystemService(NotificationManager.class);
if (mPreviousNotificationPolicyAccessPackage != null) {
nm.setNotificationPolicyAccessGranted(mPreviousNotificationPolicyAccessPackage, false);
mPreviousNotificationPolicyAccessPackage = null;
}
}
private void grantNotificationListenerAccess(String pkg, UserHandle userId) {
PackageManager pm = mContext.getPackageManager();
ArraySet<ComponentName> possibleServices = EnabledComponentsObserver.loadComponentNames(pm,
userId.getIdentifier(), NotificationListenerService.SERVICE_INTERFACE,
android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);
ContentResolver resolver = mContext.getContentResolver();
ArraySet<String> current = getCurrentNotifListeners(resolver);
mPreviousToggledListenerSettings.clear();
for (ComponentName c : possibleServices) {
String flatName = c.flattenToString();
if (Objects.equals(c.getPackageName(), pkg)
&& !current.contains(flatName)) {
mPreviousToggledListenerSettings.add(flatName);
current.add(flatName);
}
} }
if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) { if (current.size() > 0) {
mCurrentVrModeComponent = calling; String flatSettings = formatSettings(current);
mCurrentVrModeUser = userId; Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
sendUpdatedCaller = true; flatSettings);
} }
if (mCurrentVrService != null && sendUpdatedCaller) {
final ComponentName c = mCurrentVrModeComponent;
mCurrentVrService.sendEvent(new PendingEvent() {
@Override
public void runEvent(IInterface service) throws RemoteException {
IVrListener l = (IVrListener) service;
l.focusedActivityChanged(c);
}
});
}
return validUserComponent;
} }
private void revokeNotificiationListenerAccess() {
if (mPreviousToggledListenerSettings.isEmpty()) {
return;
}
ContentResolver resolver = mContext.getContentResolver();
ArraySet<String> current = getCurrentNotifListeners(resolver);
current.removeAll(mPreviousToggledListenerSettings);
mPreviousToggledListenerSettings.clear();
String flatSettings = formatSettings(current);
Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
flatSettings);
}
private ArraySet<String> getCurrentNotifListeners(ContentResolver resolver) {
String flat = Settings.Secure.getString(resolver,
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
ArraySet<String> current = new ArraySet<>();
if (flat != null) {
String[] allowed = flat.split(":");
for (String s : allowed) {
current.add(s);
}
}
return current;
}
private static String formatSettings(Collection<String> c) {
if (c == null || c.isEmpty()) {
return "";
}
StringBuilder b = new StringBuilder();
boolean start = true;
for (String s : c) {
if ("".equals(s)) {
continue;
}
if (!start) {
b.append(':');
}
b.append(s);
start = false;
}
return b.toString();
}
private void createAndConnectService(@NonNull ComponentName component, int userId) { private void createAndConnectService(@NonNull ComponentName component, int userId) {
mCurrentVrService = VrManagerService.create(mContext, component, userId); mCurrentVrService = VrManagerService.create(mContext, component, userId);
mCurrentVrService.connect(); mCurrentVrService.connect();