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