1 /*
2  * Copyright (C) 2023 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 
17 package com.android.devicelockcontroller.storage;
18 
19 import static com.android.devicelockcontroller.storage.ISetupParametersService.Stub.asInterface;
20 
21 import android.annotation.SuppressLint;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.os.Bundle;
25 
26 import androidx.annotation.GuardedBy;
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 import androidx.annotation.VisibleForTesting;
30 
31 import com.android.devicelockcontroller.DeviceLockControllerApplication;
32 import com.android.devicelockcontroller.common.DeviceLockConstants.ProvisioningType;
33 
34 import com.google.common.util.concurrent.ListenableFuture;
35 import com.google.common.util.concurrent.ListeningExecutorService;
36 import com.google.common.util.concurrent.MoreExecutors;
37 
38 import java.util.List;
39 import java.util.concurrent.Executors;
40 
41 /**
42  * Class used to access Setup Parameters from any users.
43  * Storage is hosted by user 0 and is accessed indirectly using a service.
44  */
45 public final class SetupParametersClient extends DlcClient
46         implements SetupParametersClientInterface {
47     private static final Object sInstanceLock = new Object();
48 
49     @SuppressLint("StaticFieldLeak") // Only holds application context.
50     @GuardedBy("sInstanceLock")
51     private static SetupParametersClient sClient;
52 
SetupParametersClient(@onNull Context context, ListeningExecutorService executorService)53     private SetupParametersClient(@NonNull Context context,
54             ListeningExecutorService executorService) {
55         super(context, new ComponentName(context, SetupParametersService.class), executorService);
56     }
57 
58     /**
59      * Get the SetupParametersClient singleton instance.
60      */
getInstance()61     public static SetupParametersClient getInstance() {
62         return getInstance(DeviceLockControllerApplication.getAppContext(),
63                 /* executorService= */ null);
64     }
65 
66     /**
67      * Get the SetupParametersClient singleton instance.
68      */
69     @VisibleForTesting
getInstance(Context appContext, @Nullable ListeningExecutorService executorService)70     public static SetupParametersClient getInstance(Context appContext,
71             @Nullable ListeningExecutorService executorService) {
72         synchronized (sInstanceLock) {
73             if (sClient == null) {
74                 sClient = new SetupParametersClient(
75                         appContext,
76                         executorService == null
77                                 ? MoreExecutors.listeningDecorator(Executors.newCachedThreadPool())
78                                 : executorService);
79             }
80             return sClient;
81         }
82     }
83 
84     /**
85      * Reset the Client singleton instance
86      */
87     @VisibleForTesting
reset()88     public static void reset() {
89         synchronized (sInstanceLock) {
90             if (sClient != null) {
91                 sClient.tearDown();
92                 sClient = null;
93             }
94         }
95     }
96 
97     /**
98      * Override setup parameters if there exists any; otherwise create new parameters.
99      * Note that this API can only be called in debuggable build for debugging purpose.
100      */
101     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
overridePrefs(Bundle bundle)102     public ListenableFuture<Void> overridePrefs(Bundle bundle) {
103         return call(() -> {
104             asInterface(getService()).overridePrefs(bundle);
105             return null;
106         });
107     }
108 
109     /**
110      * Dump current values of SetupParameters to logcat.
111      * Note that this API can only be called in debuggable build for debugging purpose.
112      */
113     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
dump()114     public ListenableFuture<Void> dump() {
115         return call(() -> {
116             asInterface(getService()).dump();
117             return null;
118         });
119     }
120 
121     /**
122      * Clear any existing setup parameters.
123      * Note that this API can only be called in debuggable build for debugging purpose.
124      */
125     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
126     public ListenableFuture<Void> clear() {
127         return call(() -> {
128             asInterface(getService()).clear();
129             return null;
130         });
131     }
132 
133     /**
134      * Parse setup parameters from the extras bundle.
135      *
136      * @param bundle Bundle with provisioning parameters.
137      */
138     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
139     public ListenableFuture<Void> createPrefs(Bundle bundle) {
140         return call(() -> {
141             asInterface(getService()).createPrefs(bundle);
142             return null;
143         });
144     }
145 
146     /**
147      * Get the name of the package implementing the kiosk app.
148      *
149      * @return kiosk app package name.
150      */
151     @Override
152     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
153     public ListenableFuture<String> getKioskPackage() {
154         return call(() -> asInterface(getService()).getKioskPackage());
155     }
156 
157     /**
158      * Check if the configuration disables outgoing calls.
159      *
160      * @return True if outgoign calls are disabled.
161      */
162     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
163     public ListenableFuture<Boolean> getOutgoingCallsDisabled() {
164         return call(() -> asInterface(getService()).getOutgoingCallsDisabled());
165     }
166 
167     /**
168      * Get package allowlist provisioned by the server.
169      *
170      * @return List of allowed packages.
171      */
172     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
173     public ListenableFuture<List<String>> getKioskAllowlist() {
174         return call(() -> asInterface(getService()).getKioskAllowlist());
175     }
176 
177     /**
178      * Check if notifications are enabled in lock task mode.
179      *
180      * @return True if notification are enabled.
181      */
182     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
183     public ListenableFuture<Boolean> isNotificationsInLockTaskModeEnabled() {
184         return call(() -> asInterface(getService()).isNotificationsInLockTaskModeEnabled());
185     }
186 
187     /**
188      * Check if adb debugging is allowed even on prod devices.
189      *
190      * @return True if adb debugging is allowed
191      */
192     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
193     public ListenableFuture<Boolean> isDebuggingAllowed() {
194         return call(() -> asInterface(getService()).isDebuggingAllowed());
195     }
196 
197     /**
198      * Get the provisioning type of this configuration.
199      *
200      * @return The type of provisioning which could be one of {@link ProvisioningType}.
201      */
202     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
203     public ListenableFuture<@ProvisioningType Integer> getProvisioningType() {
204         return call(() -> asInterface(getService()).getProvisioningType());
205     }
206 
207     /**
208      * Check if provision is mandatory.
209      *
210      * @return True if the provision should be mandatory.
211      */
212     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
213     public ListenableFuture<Boolean> isProvisionMandatory() {
214         return call(() -> asInterface(getService()).isProvisionMandatory());
215     }
216 
217     /**
218      * Get the name of the provider of the kiosk app.
219      *
220      * @return the name of the provider.
221      */
222     @Nullable
223     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
224     public ListenableFuture<String> getKioskAppProviderName() {
225         return call(() -> asInterface(getService()).getKioskAppProviderName());
226     }
227 
228     /**
229      * Check if installing from unknown sources should be disallowed on this device after
230      * provision
231      *
232      * @return True if installing from unknown sources is disallowed.
233      */
234     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
235     public ListenableFuture<Boolean> isInstallingFromUnknownSourcesDisallowed() {
236         return call(() -> asInterface(getService()).isInstallingFromUnknownSourcesDisallowed());
237     }
238 
239     /**
240      * Get the Terms and Conditions URL of the partner for enrolling in a Device Lock program.
241      *
242      * @return The URL to the terms and conditions.
243      */
244     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
245     public ListenableFuture<String> getTermsAndConditionsUrl() {
246         return call(() -> asInterface(getService()).getTermsAndConditionsUrl());
247     }
248 
249     /**
250      * The URL to the support page the user can use to get help.
251      *
252      * @return The URL to the support page.
253      */
254     @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone).
255     public ListenableFuture<String> getSupportUrl() {
256         return call(() -> asInterface(getService()).getSupportUrl());
257     }
258 }
259