content: /**
   * 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();
          }
      }
  
  }
  