Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f7e3815 authored by Ruben Brunk's avatar Ruben Brunk Committed by Android (Google) Code Review
Browse files

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

parents ae310b46 98576cf9
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server;
import static com.android.internal.util.ArrayUtils.appendInt;

import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
@@ -115,6 +116,9 @@ public class SystemConfig {
    // These are the packages that should not run under system user
    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() {
        synchronized (SystemConfig.class) {
            if (sInstance == null) {
@@ -168,6 +172,10 @@ public class SystemConfig {
        return mSystemUserBlacklistedApps;
    }

    public ArraySet<ComponentName> getDefaultVrComponents() {
        return mDefaultVrComponents;
    }

    SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
@@ -431,6 +439,19 @@ public class SystemConfig {
                        mSystemUserBlacklistedApps.add(pkgname);
                    }
                    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 {
                    XmlUtils.skipCurrentTag(parser);
                    continue;
+32 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.backup.BackupManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -63,6 +64,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.providers.settings.SettingsState.Setting;
import com.android.server.SystemConfig;

import java.io.File;
import java.io.FileDescriptor;
@@ -1940,7 +1942,7 @@ public class SettingsProvider extends ContentProvider {
        }

        private final class UpgradeController {
            private static final int SETTINGS_VERSION = 125;
            private static final int SETTINGS_VERSION = 126;

            private final int mUserId;

@@ -2136,6 +2138,35 @@ public class SettingsProvider extends ContentProvider {
                    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.

                // Return the current version.
+11 −5
Original line number Diff line number Diff line
@@ -227,10 +227,11 @@ public class EnabledComponentsObserver implements SettingChangeListener {
        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<>();
        PackageManager pm = mContext.getPackageManager();
        Intent queryIntent = new Intent(mServiceName);
        Intent queryIntent = new Intent(serviceName);
        List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
                queryIntent,
                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
@@ -241,10 +242,10 @@ public class EnabledComponentsObserver implements SettingChangeListener {
                ServiceInfo info = resolveInfo.serviceInfo;

                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
                            + ": it does not require the permission "
                            + mServicePermission);
                            + permissionName);
                    continue;
                }
                installed.add(component);
@@ -253,6 +254,11 @@ public class EnabledComponentsObserver implements SettingChangeListener {
        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();
+264 −41
Original line number Diff line number Diff line
@@ -16,16 +16,23 @@
package com.android.server.vr;

import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.annotation.NonNull;
import android.content.Context;
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.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.vr.IVrListener;
import android.service.vr.VrListenerService;
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.BinderChecker;

import java.lang.StringBuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;

@@ -53,8 +62,8 @@ import java.util.Set;
 *  hardware/libhardware/modules/vr
 * <p/>
 * 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
 * while in VR mode by implementing {@link android.service.vr.VrListenerService}.
 * {@link android.app.Activity#setVrModeEnabled)}.  An application may also implement a service to
 * be run while in VR mode by implementing {@link android.service.vr.VrListenerService}.
 *
 * @see {@link android.service.vr.VrListenerService}
 * @see {@link com.android.server.vr.VrManagerInternal}
@@ -74,13 +83,18 @@ public class VrManagerService extends SystemService implements EnabledComponentC
    private final IBinder mOverlayToken = new Binder();

    // State protected by mLock
    private boolean mVrModeEnabled = false;
    private boolean mVrModeEnabled;
    private final Set<VrStateListener> mListeners = new ArraySet<>();
    private EnabledComponentsObserver mComponentObserver;
    private ManagedApplicationService mCurrentVrService;
    private Context mContext;
    private ComponentName mCurrentVrModeComponent;
    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() {
        @Override
@@ -239,10 +253,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC
     *
     * @return {@code true} if the component/user combination specified is valid.
     */
    private boolean updateCurrentVrServiceLocked(boolean enabled,
            @NonNull ComponentName component, int userId, ComponentName calling) {
    private boolean updateCurrentVrServiceLocked(boolean enabled, @NonNull ComponentName component,
            int userId, ComponentName calling) {

        boolean sendUpdatedCaller = false;
        final long identity = Binder.clearCallingIdentity();
        try {

            boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
                    EnabledComponentsObserver.NO_ERROR);
@@ -256,21 +272,29 @@ public class VrManagerService extends SystemService implements EnabledComponentC
                    Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
                            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());
                        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);
                    enableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
                            new UserHandle(mCurrentVrService.getUserId()));
                    sendUpdatedCaller = true;
                }
            }
@@ -293,7 +317,206 @@ public class VrManagerService extends SystemService implements EnabledComponentC
            }

            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 (current.size() > 0) {
            String flatSettings = formatSettings(current);
            Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
                    flatSettings);
        }
    }

    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) {
        mCurrentVrService = VrManagerService.create(mContext, component, userId);