content: /*
   * Copyright (C) 2014 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.restrictions;
  
  import android.app.AppGlobals;
  import android.app.admin.IDevicePolicyManager;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IRestrictionsManager;
  import android.content.RestrictionsManager;
  import android.content.pm.ResolveInfo;
  import android.os.Binder;
  import android.os.Bundle;
  import android.os.IUserManager;
  import android.os.PersistableBundle;
  import android.os.RemoteException;
  import android.os.UserHandle;
  import android.util.Log;
  
  import com.android.internal.util.ArrayUtils;
  import com.android.server.SystemService;
  
  /**
   * SystemService wrapper for the RestrictionsManager implementation. Publishes the
   * Context.RESTRICTIONS_SERVICE.
   */
  public final class RestrictionsManagerService extends SystemService {
  
      static final String LOG_TAG = "RestrictionsManagerService";
      static final boolean DEBUG = false;
  
      private final RestrictionsManagerImpl mRestrictionsManagerImpl;
  
      public RestrictionsManagerService(Context context) {
          super(context);
          mRestrictionsManagerImpl = new RestrictionsManagerImpl(context);
      }
  
      @Override
      public void onStart() {
          publishBinderService(Context.RESTRICTIONS_SERVICE, mRestrictionsManagerImpl);
      }
  
      class RestrictionsManagerImpl extends IRestrictionsManager.Stub {
          final Context mContext;
          private final IUserManager mUm;
          private final IDevicePolicyManager mDpm;
  
          public RestrictionsManagerImpl(Context context) {
              mContext = context;
              mUm = (IUserManager) getBinderService(Context.USER_SERVICE);
              mDpm = (IDevicePolicyManager) getBinderService(Context.DEVICE_POLICY_SERVICE);
          }
  
          @Override
          public Bundle getApplicationRestrictions(String packageName) throws RemoteException {
              return mUm.getApplicationRestrictions(packageName);
          }
  
          @Override
          public boolean hasRestrictionsProvider() throws RemoteException {
              int userHandle = UserHandle.getCallingUserId();
              if (mDpm != null) {
                  long ident = Binder.clearCallingIdentity();
                  try {
                      return mDpm.getRestrictionsProvider(userHandle) != null;
                  } finally {
                      Binder.restoreCallingIdentity(ident);
                  }
              } else {
                  return false;
              }
          }
  
          @Override
          public void requestPermission(final String packageName, final String requestType,
                  final String requestId,
                  final PersistableBundle requestData) throws RemoteException {
              if (DEBUG) {
                  Log.i(LOG_TAG, "requestPermission");
              }
              int callingUid = Binder.getCallingUid();
              int userHandle = UserHandle.getUserId(callingUid);
              if (mDpm != null) {
                  long ident = Binder.clearCallingIdentity();
                  try {
                      ComponentName restrictionsProvider =
                              mDpm.getRestrictionsProvider(userHandle);
                      // Check if there is a restrictions provider
                      if (restrictionsProvider == null) {
                          throw new IllegalStateException(
                              "Cannot request permission without a restrictions provider registered");
                      }
                      // Check that the packageName matches the caller.
                      enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
                              " match caller ");
                      // Prepare and broadcast the intent to the provider
                      Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_PERMISSION);
                      intent.setComponent(restrictionsProvider);
                      intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
                      intent.putExtra(RestrictionsManager.EXTRA_REQUEST_TYPE, requestType);
                      intent.putExtra(RestrictionsManager.EXTRA_REQUEST_ID, requestId);
                      intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
                      mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
                  } finally {
                      Binder.restoreCallingIdentity(ident);
                  }
              }
          }
  
          @Override
          public Intent createLocalApprovalIntent() throws RemoteException {
              if (DEBUG) {
                  Log.i(LOG_TAG, "requestPermission");
              }
              final int userHandle = UserHandle.getCallingUserId();
              if (mDpm != null) {
                  long ident = Binder.clearCallingIdentity();
                  try {
                      ComponentName restrictionsProvider =
                              mDpm.getRestrictionsProvider(userHandle);
                      // Check if there is a restrictions provider
                      if (restrictionsProvider == null) {
                          throw new IllegalStateException(
                              "Cannot request permission without a restrictions provider registered");
                      }
                      String providerPackageName = restrictionsProvider.getPackageName();
                      Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_LOCAL_APPROVAL);
                      intent.setPackage(providerPackageName);
                      ResolveInfo ri = AppGlobals.getPackageManager().resolveIntent(intent,
                              null /* resolvedType */, 0 /* flags */, userHandle);
                      if (ri != null && ri.activityInfo != null && ri.activityInfo.exported) {
                          intent.setComponent(new ComponentName(ri.activityInfo.packageName,
                                  ri.activityInfo.name));
                          return intent;
                      }
                  } finally {
                      Binder.restoreCallingIdentity(ident);
                  }
              }
              return null;
          }
  
          @Override
          public void notifyPermissionResponse(String packageName, PersistableBundle response)
                  throws RemoteException {
              // Check caller
              int callingUid = Binder.getCallingUid();
              int userHandle = UserHandle.getUserId(callingUid);
              if (mDpm != null) {
                  long ident = Binder.clearCallingIdentity();
                  try {
                      ComponentName permProvider = mDpm.getRestrictionsProvider(userHandle);
                      if (permProvider == null) {
                          throw new SecurityException("No restrictions provider registered for user");
                      }
                      enforceCallerMatchesPackage(callingUid, permProvider.getPackageName(),
                              "Restrictions provider does not match caller ");
  
                      // Post the response to target package
                      Intent responseIntent = new Intent(
                              RestrictionsManager.ACTION_PERMISSION_RESPONSE_RECEIVED);
                      responseIntent.setPackage(packageName);
                      responseIntent.putExtra(RestrictionsManager.EXTRA_RESPONSE_BUNDLE, response);
                      mContext.sendBroadcastAsUser(responseIntent, new UserHandle(userHandle));
                  } finally {
                      Binder.restoreCallingIdentity(ident);
                  }
              }
          }
  
          private void enforceCallerMatchesPackage(int callingUid, String packageName,
                  String message) {
              try {
                  String[] pkgs = AppGlobals.getPackageManager().getPackagesForUid(callingUid);
                  if (pkgs != null) {
                      if (!ArrayUtils.contains(pkgs, packageName)) {
                          throw new SecurityException(message + callingUid);
                      }
                  }
              } catch (RemoteException re) {
                  // Shouldn't happen
              }
          }
      }
  }
  