1 /* 2 * Copyright 2017, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.managedprovisioning.preprovisioning.terms; 17 18 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DISCLAIMER_CONTENT; 19 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DISCLAIMER_HEADER; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.annotation.IntDef; 24 import android.content.Context; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageManager; 27 import android.content.res.Resources; 28 29 import com.android.managedprovisioning.R; 30 import com.android.managedprovisioning.common.ProvisionLogger; 31 import com.android.managedprovisioning.common.StoreUtils; 32 import com.android.managedprovisioning.common.Utils; 33 import com.android.managedprovisioning.model.DisclaimersParam; 34 import com.android.managedprovisioning.model.ProvisioningParams; 35 36 import java.io.File; 37 import java.io.IOException; 38 import java.lang.annotation.Retention; 39 import java.lang.annotation.RetentionPolicy; 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.Objects; 43 import java.util.stream.Collectors; 44 45 /** 46 * Sources all available {@link TermsDocument}s: 47 * <ul> 48 * <li> hardcoded 'General' terms, 49 * <li> terms exposed via installed apps, 50 * <li> terms passed from DPC. 51 * </ul> 52 */ 53 public class TermsProvider { 54 private final Context mContext; 55 private final StoreUtils.TextFileReader mTextFileReader; 56 private final ProvisioningParams mParams; 57 private final Utils mUtils; 58 private final Injector mInjector; 59 60 /** 61 * Sources all available {@link TermsDocument}s: 62 * <ul> 63 * <li> hardcoded 'General' terms, 64 * <li> terms exposed via installed apps, 65 * <li> terms passed from DPC. 66 * </ul> 67 */ TermsProvider(Context context, StoreUtils.TextFileReader textFileReader, ProvisioningParams params, Utils utils, Injector injector)68 public TermsProvider(Context context, StoreUtils.TextFileReader textFileReader, 69 ProvisioningParams params, Utils utils, Injector injector) { 70 mContext = requireNonNull(context); 71 mTextFileReader = requireNonNull(textFileReader); 72 mParams = requireNonNull(params); 73 mUtils = requireNonNull(utils); 74 mInjector = requireNonNull(injector); 75 } 76 77 /** 78 * Sources all available {@link TermsDocument}s: 79 * <ul> 80 * <li> hardcoded 'General' terms, 81 * <li> terms exposed via installed apps, 82 * <li> terms passed from DPC. 83 * </ul> 84 */ getTerms()85 public List<TermsDocument> getTerms() { 86 List<TermsDocument> result = new ArrayList<>(); 87 int provisioningCase = determineProvisioningCase(mParams); 88 89 if (provisioningCase == ProvisioningCase.DEVICE_OWNER) { 90 result.addAll(getSystemAppTerms()); 91 } 92 93 result.addAll(getExtraDisclaimers(mParams)); 94 95 return result.stream().filter(Objects::nonNull).collect(Collectors.toList()); 96 } 97 98 /** 99 * Returns a generic disclaimer relative to the provisioning mode. 100 */ getGeneralDisclaimer()101 public TermsDocument getGeneralDisclaimer() { 102 int provisioningCase = determineProvisioningCase(mParams); 103 String heading = mContext.getString(provisioningCase == ProvisioningCase.PROFILE_OWNER 104 ? R.string.work_profile_info 105 : R.string.managed_device_info); 106 String content = mContext.getString(provisioningCase == ProvisioningCase.PROFILE_OWNER 107 ? R.string.admin_has_ability_to_monitor_profile 108 : R.string.admin_has_ability_to_monitor_device); 109 return TermsDocument.createInstance(heading, content); 110 } 111 determineProvisioningCase(ProvisioningParams params)112 private int determineProvisioningCase(ProvisioningParams params) { 113 if (mUtils.isDeviceOwnerAction(params.provisioningAction)) { 114 return ProvisioningCase.DEVICE_OWNER; 115 } 116 return ProvisioningCase.PROFILE_OWNER; 117 } 118 getSystemAppTerms()119 private List<TermsDocument> getSystemAppTerms() { 120 List<TermsDocument> terms = new ArrayList<>(); 121 List<ApplicationInfo> appInfos = mInjector.getInstalledApplications(); 122 for (ApplicationInfo appInfo : appInfos) { 123 String header = getStringMetaData(appInfo, EXTRA_PROVISIONING_DISCLAIMER_HEADER); 124 String content = getStringMetaData(appInfo, EXTRA_PROVISIONING_DISCLAIMER_CONTENT); 125 if (header != null && content != null) { 126 terms.add(TermsDocument.createInstance(header, content)); 127 } 128 } 129 return terms; 130 } 131 getExtraDisclaimers(ProvisioningParams params)132 private List<TermsDocument> getExtraDisclaimers(ProvisioningParams params) { 133 List<TermsDocument> result = new ArrayList<>(); 134 135 DisclaimersParam.Disclaimer[] disclaimers = params.disclaimersParam == null ? null 136 : params.disclaimersParam.mDisclaimers; 137 if (disclaimers != null) { 138 for (DisclaimersParam.Disclaimer disclaimer : disclaimers) { 139 try { 140 String htmlContent = mTextFileReader.read( 141 new File(disclaimer.mContentFilePath)); 142 result.add(TermsDocument.createInstance(disclaimer.mHeader, htmlContent)); 143 } catch (IOException e) { 144 ProvisionLogger.loge("Failed to read disclaimer", e); 145 } 146 } 147 } 148 149 return result; 150 } 151 getStringMetaData(ApplicationInfo appInfo, String key)152 private String getStringMetaData(ApplicationInfo appInfo, String key) { 153 if (appInfo.metaData != null) { 154 int resId = appInfo.metaData.getInt(key); 155 if (resId != 0) { 156 try { 157 return mContext.getPackageManager().getResourcesForApplication( 158 appInfo).getString(resId); 159 } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { 160 ProvisionLogger.loge("NameNotFoundException", e); 161 } 162 } 163 } 164 return null; 165 } 166 167 // TODO: move somewhere more general 168 @IntDef(value = { 169 ProvisioningCase.PROFILE_OWNER, 170 ProvisioningCase.DEVICE_OWNER, 171 }) 172 @Retention(RetentionPolicy.SOURCE) 173 private @interface ProvisioningCase { 174 int PROFILE_OWNER = 1; 175 int DEVICE_OWNER = 2; 176 } 177 178 interface Injector { getInstalledApplications()179 List<ApplicationInfo> getInstalledApplications(); 180 } 181 }