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 com.google.android.setupdesign.util.DeviceHelper; 37 38 import java.io.File; 39 import java.io.IOException; 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 42 import java.util.ArrayList; 43 import java.util.List; 44 import java.util.Objects; 45 import java.util.stream.Collectors; 46 47 /** 48 * Sources all available {@link TermsDocument}s: 49 * <ul> 50 * <li> hardcoded 'General' terms, 51 * <li> terms exposed via installed apps, 52 * <li> terms passed from DPC. 53 * </ul> 54 */ 55 public class TermsProvider { 56 private final Context mContext; 57 private final StoreUtils.TextFileReader mTextFileReader; 58 private final ProvisioningParams mParams; 59 private final Utils mUtils; 60 private final Injector mInjector; 61 62 /** 63 * Sources all available {@link TermsDocument}s: 64 * <ul> 65 * <li> hardcoded 'General' terms, 66 * <li> terms exposed via installed apps, 67 * <li> terms passed from DPC. 68 * </ul> 69 */ TermsProvider(Context context, StoreUtils.TextFileReader textFileReader, ProvisioningParams params, Utils utils, Injector injector)70 public TermsProvider(Context context, StoreUtils.TextFileReader textFileReader, 71 ProvisioningParams params, Utils utils, Injector injector) { 72 mContext = requireNonNull(context); 73 mTextFileReader = requireNonNull(textFileReader); 74 mParams = requireNonNull(params); 75 mUtils = requireNonNull(utils); 76 mInjector = requireNonNull(injector); 77 } 78 79 /** 80 * Sources all available {@link TermsDocument}s: 81 * <ul> 82 * <li> hardcoded 'General' terms, 83 * <li> terms exposed via installed apps, 84 * <li> terms passed from DPC. 85 * </ul> 86 */ getTerms()87 public List<TermsDocument> getTerms() { 88 List<TermsDocument> result = new ArrayList<>(); 89 int provisioningCase = determineProvisioningCase(mParams); 90 91 if (provisioningCase == ProvisioningCase.DEVICE_OWNER) { 92 result.addAll(getSystemAppTerms()); 93 } 94 95 result.addAll(getExtraDisclaimers(mParams)); 96 97 return result.stream().filter(Objects::nonNull).collect(Collectors.toList()); 98 } 99 100 /** 101 * Returns a generic disclaimer relative to the provisioning mode. 102 */ getGeneralDisclaimer()103 public TermsDocument getGeneralDisclaimer() { 104 int provisioningCase = determineProvisioningCase(mParams); 105 CharSequence deviceName = DeviceHelper.getDeviceName(mContext); 106 String heading = 107 mContext.getString( 108 provisioningCase == ProvisioningCase.PROFILE_OWNER 109 ? R.string.work_profile_info 110 : R.string.managed_device_info); 111 String content = 112 mContext.getString( 113 provisioningCase == ProvisioningCase.PROFILE_OWNER 114 ? R.string.admin_has_ability_to_monitor_profile 115 : R.string.admin_has_ability_to_monitor_device, 116 deviceName); 117 return TermsDocument.createInstance(heading, content); 118 } 119 determineProvisioningCase(ProvisioningParams params)120 private int determineProvisioningCase(ProvisioningParams params) { 121 if (mUtils.isDeviceOwnerAction(params.provisioningAction)) { 122 return ProvisioningCase.DEVICE_OWNER; 123 } 124 return ProvisioningCase.PROFILE_OWNER; 125 } 126 getSystemAppTerms()127 private List<TermsDocument> getSystemAppTerms() { 128 List<TermsDocument> terms = new ArrayList<>(); 129 List<ApplicationInfo> appInfos = mInjector.getInstalledApplications(); 130 for (ApplicationInfo appInfo : appInfos) { 131 String header = getStringMetaData(appInfo, EXTRA_PROVISIONING_DISCLAIMER_HEADER); 132 String content = getStringMetaData(appInfo, EXTRA_PROVISIONING_DISCLAIMER_CONTENT); 133 if (header != null && content != null) { 134 terms.add(TermsDocument.createInstance(header, content)); 135 } 136 } 137 return terms; 138 } 139 getExtraDisclaimers(ProvisioningParams params)140 private List<TermsDocument> getExtraDisclaimers(ProvisioningParams params) { 141 List<TermsDocument> result = new ArrayList<>(); 142 143 DisclaimersParam.Disclaimer[] disclaimers = params.disclaimersParam == null ? null 144 : params.disclaimersParam.mDisclaimers; 145 if (disclaimers != null) { 146 for (DisclaimersParam.Disclaimer disclaimer : disclaimers) { 147 try { 148 String htmlContent = mTextFileReader.read( 149 new File(disclaimer.mContentFilePath)); 150 result.add(TermsDocument.createInstance(disclaimer.mHeader, htmlContent)); 151 } catch (IOException e) { 152 ProvisionLogger.loge("Failed to read disclaimer", e); 153 } 154 } 155 } 156 157 return result; 158 } 159 getStringMetaData(ApplicationInfo appInfo, String key)160 private String getStringMetaData(ApplicationInfo appInfo, String key) { 161 if (appInfo.metaData != null) { 162 int resId = appInfo.metaData.getInt(key); 163 if (resId != 0) { 164 try { 165 return mContext.getPackageManager().getResourcesForApplication( 166 appInfo).getString(resId); 167 } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { 168 ProvisionLogger.loge("NameNotFoundException", e); 169 } 170 } 171 } 172 return null; 173 } 174 175 // TODO: move somewhere more general 176 @IntDef(value = { 177 ProvisioningCase.PROFILE_OWNER, 178 ProvisioningCase.DEVICE_OWNER, 179 }) 180 @Retention(RetentionPolicy.SOURCE) 181 private @interface ProvisioningCase { 182 int PROFILE_OWNER = 1; 183 int DEVICE_OWNER = 2; 184 } 185 186 interface Injector { getInstalledApplications()187 List<ApplicationInfo> getInstalledApplications(); 188 } 189 } 190