content: package com.android.certification.niap.permission.dpctester.provision;
  /*
   * Copyright (C) 2017 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.
   */
  
  
  import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
  import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
  import static com.android.certification.niap.permission.dpctester.receiver.DeviceAdminReceiver.getComponentName;
  import android.accounts.Account;
  import android.accounts.AccountManager;
  import android.annotation.TargetApi;
  import android.app.admin.DevicePolicyManager;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.content.pm.PackageInfo;
  import android.content.pm.PackageManager;
  import android.content.pm.PermissionInfo;
  import android.os.Build.VERSION_CODES;
  import android.os.PersistableBundle;
  import android.util.Log;
  import com.android.certification.niap.permission.dpctester.activity.AddAccountActivity;
  import com.android.certification.niap.permission.dpctester.activity.EnableCosuActivity;
  import com.android.certification.niap.permission.dpctester.activity.FinalizeActivity;
  import com.android.certification.niap.permission.dpctester.common.LaunchIntentUtil;
  import com.android.certification.niap.permission.dpctester.common.Util;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  
  /**
   * Task executed after provisioning is done indicated by either the {@link
   * DevicePolicyManager#ACTION_PROVISIONING_SUCCESSFUL} activity intent or the {@link
   * android.app.admin.DeviceAdminReceiver#onProfileProvisioningComplete(Context, Intent)} broadcast.
   *
   * <p>Operations performed:
   *
   * <ul>
   *   <li>self-grant all run-time permissions
   *   <li>enable the launcher activity
   *   <li>start waiting for first account ready broadcast
   * </ul>
   */
  public class PostProvisioningTask {
      private static final String TAG = "PostProvisioningTask";
      private static final String SETUP_MANAGEMENT_LAUNCH_ACTIVITY =
              "com.afwsamples.testdpc.SetupManagementLaunchActivity";
      private static final String POST_PROV_PREFS = "post_prov_prefs";
      private static final String KEY_POST_PROV_DONE = "key_post_prov_done";
  
      private final Context mContext;
      private final DevicePolicyManager mDevicePolicyManager;
      private final SharedPreferences mSharedPrefs;
  
      public PostProvisioningTask(Context context) {
          mContext = context;
          mDevicePolicyManager =
                  (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
          mSharedPrefs = context.getSharedPreferences(POST_PROV_PREFS, Context.MODE_PRIVATE);
      }
  
      public boolean performPostProvisioningOperations(Intent intent) {
          if (isPostProvisioningDone()) {
              return false;
          }
          markPostProvisioningDone();
  
          // From M onwards, permissions are not auto-granted, so we need to manually grant
          // permissions for TestDPC.
          if (Util.SDK_INT >= VERSION_CODES.M) {
              autoGrantRequestedPermissionsToSelf();
          }
  
          // Retreive the admin extras bundle, which we can use to determine the original context for
          // TestDPCs launch.
          PersistableBundle extras = intent.getParcelableExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
          if (Util.SDK_INT >= VERSION_CODES.O) {
              maybeSetAffiliationIds(extras);
          }
  
          // Hide the setup launcher when this app is the admin
          mContext
                  .getPackageManager()
                  .setComponentEnabledSetting(
                          new ComponentName(mContext, SETUP_MANAGEMENT_LAUNCH_ACTIVITY),
                          PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                          PackageManager.DONT_KILL_APP);
  
          return true;
      }
  
      public Intent getPostProvisioningLaunchIntent(Intent intent) {
          // Enable the profile after provisioning is complete.
          Intent launch;
  
          // Retreive the admin extras bundle, which we can use to determine the original context for
          // TestDPCs launch.
          PersistableBundle extras = intent.getParcelableExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
          String packageName = mContext.getPackageName();
          boolean synchronousAuthLaunch = LaunchIntentUtil.isSynchronousAuthLaunch(extras);
          boolean cosuLaunch = LaunchIntentUtil.isCosuLaunch(extras);
          boolean isProfileOwner = mDevicePolicyManager.isProfileOwnerApp(packageName);
          boolean isDeviceOwner = mDevicePolicyManager.isDeviceOwnerApp(packageName);
  
          // Drop out quickly if we're neither profile or device owner.
          if (!isProfileOwner && !isDeviceOwner) {
              return null;
          }
  
          if (cosuLaunch) {
              launch = new Intent(mContext, EnableCosuActivity.class);
              launch.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, extras);
          } else {
              launch = new Intent(mContext, FinalizeActivity.class);
          }
  
          if (synchronousAuthLaunch) {
              String accountName = LaunchIntentUtil.getAddedAccountName(extras);
              if (accountName != null) {
                  launch.putExtra(LaunchIntentUtil.EXTRA_ACCOUNT_NAME, accountName);
              }
          }
  
          // For synchronous auth cases, we can assume accounts are already setup (or will be shortly,
          // as account migration for Profile Owner is asynchronous). For COSU we don't want to show
          // the account option to the user, as no accounts should be added for now.
          // In other cases, offer to add an account to the newly configured device/profile.
          if (!synchronousAuthLaunch && !cosuLaunch) {
              AccountManager accountManager = AccountManager.get(mContext);
              Account[] accounts = accountManager.getAccounts();
              if (accounts != null && accounts.length == 0) {
                  // Add account after provisioning is complete.
                  Intent addAccountIntent = new Intent(mContext, AddAccountActivity.class);
                  addAccountIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                  addAccountIntent.putExtra(AddAccountActivity.EXTRA_NEXT_ACTIVITY_INTENT, launch);
                  return addAccountIntent;
              }
          }
  
          launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          return launch;
      }
  
      private void markPostProvisioningDone() {
          mSharedPrefs.edit().putBoolean(KEY_POST_PROV_DONE, true).commit();
      }
  
      private boolean isPostProvisioningDone() {
          return mSharedPrefs.getBoolean(KEY_POST_PROV_DONE, false);
      }
  
      @TargetApi(VERSION_CODES.O)
      private void maybeSetAffiliationIds(PersistableBundle extras) {
          if (extras == null) {
              return;
          }
          String affiliationId = extras.getString(LaunchIntentUtil.EXTRA_AFFILIATION_ID);
          if (affiliationId != null) {
              mDevicePolicyManager.setAffiliationIds(
                      getComponentName(mContext), Collections.singleton(affiliationId));
          }
      }
  
      @TargetApi(VERSION_CODES.M)
      private void autoGrantRequestedPermissionsToSelf() {
          String packageName = mContext.getPackageName();
          ComponentName adminComponentName = getComponentName(mContext);
  
          List<String> permissions = getRuntimePermissions(mContext.getPackageManager(), packageName);
          for (String permission : permissions) {
              boolean success =
                      mDevicePolicyManager.setPermissionGrantState(
                              adminComponentName, packageName, permission, PERMISSION_GRANT_STATE_GRANTED);
              Log.d(TAG, "Auto-granting " + permission + ", success: " + success);
              if (!success) {
                  Log.e(TAG, "Failed to auto grant permission to self: " + permission);
              }
          }
      }
  
      private List<String> getRuntimePermissions(PackageManager packageManager, String packageName) {
          List<String> permissions = new ArrayList<>();
          PackageInfo packageInfo;
          try {
              packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
          } catch (PackageManager.NameNotFoundException e) {
              Log.e(TAG, "Could not retrieve info about the package: " + packageName, e);
              return permissions;
          }
  
          if (packageInfo != null && packageInfo.requestedPermissions != null) {
              for (String requestedPerm : packageInfo.requestedPermissions) {
                  if (isRuntimePermission(packageManager, requestedPerm)) {
                      permissions.add(requestedPerm);
                  }
              }
          }
          return permissions;
      }
  
      private boolean isRuntimePermission(PackageManager packageManager, String permission) {
          try {
              PermissionInfo pInfo = packageManager.getPermissionInfo(permission, 0);
              if (pInfo != null) {
                  if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                          == PermissionInfo.PROTECTION_DANGEROUS) {
                      return true;
                  }
              }
          } catch (PackageManager.NameNotFoundException e) {
              Log.i(TAG, "Could not retrieve info about the permission: " + permission);
          }
          return false;
      }
  }
  
  