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.provision.grpc;
18 
19 import android.content.Context;
20 import android.content.SharedPreferences;
21 import android.os.Build;
22 import android.os.SystemProperties;
23 import android.os.UserHandle;
24 import android.util.Pair;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 import androidx.annotation.WorkerThread;
29 
30 import com.android.devicelockcontroller.provision.grpc.impl.DeviceFinalizeClientImpl;
31 import com.android.devicelockcontroller.util.LogUtil;
32 
33 import io.grpc.ClientInterceptor;
34 import io.grpc.Status;
35 
36 /**
37  * An abstract class that's intended for implementation of class that manages communication with
38  * Device finalize service.
39  */
40 public abstract class DeviceFinalizeClient {
41     public static final String TAG = "DeviceFinalizeClient";
42     private static final String FILENAME = "debug-finalize-preferences";
43     private static final String HOST_NAME_OVERRIDE = "host.name.override";
44     public static final String DEVICE_FINALIZE_CLIENT_DEBUG_CLASS_NAME =
45             "com.android.devicelockcontroller.debug.DeviceFinalizeClientDebug";
46     private static volatile DeviceFinalizeClient sClient;
47     protected static String sRegisteredId = "";
48     protected static String sHostName = "";
49     protected static int sPortNumber = 0;
50     protected static Pair<String, String> sApiKey = new Pair<>("", "");
51     private static volatile boolean sUseDebugClient;
52     @Nullable
53     private static volatile SharedPreferences sSharedPreferences;
54 
55     @Nullable
getSharedPreferences( @ullable Context context)56     private static synchronized SharedPreferences getSharedPreferences(
57             @Nullable Context context) {
58         if (sSharedPreferences == null && context != null) {
59             sSharedPreferences =
60                     context.createContextAsUser(UserHandle.SYSTEM, /* flags= */
61                             0).createDeviceProtectedStorageContext().getSharedPreferences(FILENAME,
62                             Context.MODE_PRIVATE);
63         }
64         return sSharedPreferences;
65     }
66 
67     /**
68      * Override the host name so that the client always connects to it instead
69      */
setHostNameOverride(Context context, String override)70     public static void setHostNameOverride(Context context, String override) {
71         getSharedPreferences(context).edit().putString(HOST_NAME_OVERRIDE, override).apply();
72     }
73 
74     /**
75      * Get an instance of {@link DeviceFinalizeClient} object.
76      * Note that, the arguments will be ignored after first initialization.
77      */
getInstance( Context context, String hostName, int portNumber, ClientInterceptor clientInterceptor, String registeredId)78     public static DeviceFinalizeClient getInstance(
79             Context context,
80             String hostName,
81             int portNumber,
82             ClientInterceptor clientInterceptor,
83             String registeredId) {
84         boolean useDebugClient = false;
85         String hostNameOverride = "";
86         if (Build.isDebuggable()) {
87             useDebugClient =
88                     SystemProperties.getBoolean("debug.devicelock.finalize", /* def= */ false);
89             hostNameOverride = getSharedPreferences(context).getString(
90                     HOST_NAME_OVERRIDE, /* def= */ "");
91             if (!hostNameOverride.isEmpty()) {
92                 hostName = hostNameOverride;
93             }
94         }
95         if (sClient == null || sUseDebugClient != useDebugClient) {
96             synchronized (DeviceFinalizeClient.class) {
97                 // In case the initialization is already done by other thread use existing
98                 // instance.
99                 if (sClient != null && sUseDebugClient == useDebugClient) {
100                     return sClient;
101                 }
102                 sHostName = hostName;
103                 sPortNumber = portNumber;
104                 sRegisteredId = registeredId;
105                 sUseDebugClient = useDebugClient;
106                 try {
107                     if (Build.isDebuggable() && sUseDebugClient) {
108                         final String className = DEVICE_FINALIZE_CLIENT_DEBUG_CLASS_NAME;
109                         LogUtil.d(TAG, "Creating instance for " + className);
110                         Class<?> clazz = Class.forName(className);
111                         sClient =
112                                 (DeviceFinalizeClient) clazz.getDeclaredConstructor().newInstance();
113                     } else {
114                         sClient = new DeviceFinalizeClientImpl(clientInterceptor);
115                     }
116 
117                 } catch (Exception e) {
118                     throw new RuntimeException("Failed to get DeviceFinalizeClient instance", e);
119                 }
120             }
121         }
122         return sClient;
123     }
124 
125     /**
126      * Reports that a device completed a Device Lock program.
127      */
128     @WorkerThread
reportDeviceProgramComplete()129     public abstract ReportDeviceProgramCompleteResponse reportDeviceProgramComplete();
130 
131     /**
132      * Class that used to indicate the successfulness / failure status of the response.
133      */
134     public static final class ReportDeviceProgramCompleteResponse extends
135             GrpcResponse {
ReportDeviceProgramCompleteResponse()136         public ReportDeviceProgramCompleteResponse() {
137             super();
138         }
139 
ReportDeviceProgramCompleteResponse(@onNull Status status)140         public ReportDeviceProgramCompleteResponse(@NonNull Status status) {
141             super(status);
142         }
143     }
144 }
145