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 import static android.content.pm.PackageManager.GET_META_DATA; 21 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; 22 23 import android.annotation.IntDef; 24 import android.app.admin.DevicePolicyManager; 25 import android.content.Context; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageManager; 28 import android.content.res.Resources; 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 import java.io.File; 36 import java.io.IOException; 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.Objects; 42 import java.util.stream.Collectors; 43 44 /** 45 * Sources all available {@link TermsDocument}s: 46 * <ul> 47 * <li> hardcoded 'General' terms, 48 * <li> terms exposed via installed apps, 49 * <li> terms passed from DPC. 50 * </ul> 51 */ 52 public class TermsProvider { 53 private final Context mContext; 54 private final StoreUtils.TextFileReader mTextFileReader; 55 private final Utils mUtils; 56 57 /** 58 * Sources all available {@link TermsDocument}s: 59 * <ul> 60 * <li> hardcoded 'General' terms, 61 * <li> terms exposed via installed apps, 62 * <li> terms passed from DPC. 63 * </ul> 64 */ TermsProvider(Context context, StoreUtils.TextFileReader textFileReader, Utils utils)65 public TermsProvider(Context context, StoreUtils.TextFileReader textFileReader, Utils utils) { 66 mContext = context; 67 mTextFileReader = textFileReader; 68 mUtils = utils; 69 } 70 71 /** 72 * Sources all available {@link TermsDocument}s: 73 * <ul> 74 * <li> hardcoded 'General' terms, 75 * <li> terms exposed via installed apps, 76 * <li> terms passed from DPC. 77 * </ul> 78 */ getTerms(ProvisioningParams params, @Flags int flags)79 public List<TermsDocument> getTerms(ProvisioningParams params, @Flags int flags) { 80 List<TermsDocument> result = new ArrayList<>(); 81 int provisioningCase = determineProvisioningCase(params); 82 83 if ((flags & Flags.SKIP_GENERAL_DISCLAIMER) == 0) { 84 result.add(getGeneralDisclaimer(provisioningCase)); 85 } 86 87 if (provisioningCase == ProvisioningCase.DEVICE_OWNER) { 88 result.addAll(getSystemAppTerms()); 89 } 90 91 result.addAll(getExtraDisclaimers(params)); 92 93 return result.stream().filter(Objects::nonNull).collect(Collectors.toList()); 94 } 95 determineProvisioningCase(ProvisioningParams params)96 private int determineProvisioningCase(ProvisioningParams params) { 97 if (mUtils.isDeviceOwnerAction(params.provisioningAction)) { 98 return ProvisioningCase.DEVICE_OWNER; 99 } 100 101 // TODO: move somewhere more general 102 boolean isComp = ((DevicePolicyManager) mContext 103 .getSystemService(Context.DEVICE_POLICY_SERVICE)).isDeviceManaged(); 104 105 return isComp ? ProvisioningCase.COMP : ProvisioningCase.PROFILE_OWNER; 106 } 107 getGeneralDisclaimer(@rovisioningCase int provisioningCase)108 private TermsDocument getGeneralDisclaimer(@ProvisioningCase int provisioningCase) { 109 String heading = mContext.getString(provisioningCase == ProvisioningCase.PROFILE_OWNER 110 ? R.string.work_profile_info 111 : R.string.managed_device_info); 112 String content = mContext.getString(provisioningCase == ProvisioningCase.PROFILE_OWNER 113 ? R.string.admin_has_ability_to_monitor_profile 114 : R.string.admin_has_ability_to_monitor_device); 115 return TermsDocument.createInstance(heading, content); 116 } 117 getSystemAppTerms()118 private List<TermsDocument> getSystemAppTerms() { 119 List<TermsDocument> terms = new ArrayList<>(); 120 List<ApplicationInfo> appInfos = mContext.getPackageManager().getInstalledApplications( 121 MATCH_SYSTEM_ONLY | GET_META_DATA); 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 ProvisioningCase.COMP, 172 }) 173 @Retention(RetentionPolicy.SOURCE) 174 private @interface ProvisioningCase { 175 int PROFILE_OWNER = 1; 176 int DEVICE_OWNER = 2; 177 int COMP = 4; 178 } 179 180 @IntDef(flag = true, value = { 181 Flags.SKIP_GENERAL_DISCLAIMER, 182 }) 183 @Retention(RetentionPolicy.SOURCE) 184 public @interface Flags { 185 int SKIP_GENERAL_DISCLAIMER = 1; 186 } 187 }