1 /*
2  * Copyright (C) 2020 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.libraries.rcs.simpleclient.provisioning;
18 
19 import android.content.Context;
20 import android.content.SharedPreferences;
21 import android.os.Build.VERSION_CODES;
22 import android.telephony.SubscriptionManager;
23 import android.telephony.ims.ImsException;
24 import android.telephony.ims.ProvisioningManager;
25 import android.telephony.ims.ProvisioningManager.RcsProvisioningCallback;
26 import android.telephony.ims.RcsClientConfiguration;
27 import android.util.Log;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.RequiresApi;
31 import androidx.annotation.RequiresPermission;
32 import androidx.annotation.VisibleForTesting;
33 
34 import java.util.Optional;
35 import java.util.concurrent.ExecutorService;
36 import java.util.concurrent.Executors;
37 
38 /**
39  * "Fake" provisioning implementation for supplying a static config when testing ProvisioningManager
40  * is unnecessary. State changes are invoked manually.
41  */
42 public class StaticConfigProvisioningController implements ProvisioningController {
43 
44     private static final String TAG = StaticConfigProvisioningController.class.getSimpleName();
45     private final ProvisioningManager provisioningManager;
46     private final ExecutorService executorService = Executors.newSingleThreadExecutor();
47     private Optional<RcsProvisioningCallback> storedCallback = Optional.empty();
48     private Optional<ProvisioningStateChangeCallback> stateChangeCallback = Optional.empty();
49     private Optional<byte[]> configXmlData = Optional.empty();
50     private Context context;
51 
StaticConfigProvisioningController(int subId, Context context)52     private StaticConfigProvisioningController(int subId, Context context) {
53         this.provisioningManager = ProvisioningManager.createForSubscriptionId(subId);
54         this.context = context;
55     }
56 
57     @RequiresApi(api = VERSION_CODES.R)
createWithDefaultSubscriptionId( Context context)58     public static StaticConfigProvisioningController createWithDefaultSubscriptionId(
59             Context context) {
60         return new StaticConfigProvisioningController(
61                 SubscriptionManager.getActiveDataSubscriptionId(), context);
62     }
63 
64     /** Create ProvisioningController */
createForSubscriptionId(int subscriptionId, Context context)65     public static StaticConfigProvisioningController createForSubscriptionId(int subscriptionId,
66             Context context) {
67         return new StaticConfigProvisioningController(subscriptionId, context);
68     }
69 
70     // Static configuration.
getDefaultClientConfiguration()71     private RcsClientConfiguration getDefaultClientConfiguration() {
72         SharedPreferences pref = context.getSharedPreferences("CONFIG", context.MODE_PRIVATE);
73 
74         return new RcsClientConfiguration(
75                 /*rcsVersion=*/ pref.getString("RCS_VERSION", "6.0"),
76                 /*rcsProfile=*/ pref.getString("RCS_PROFILE", "UP_1.0"),
77                 /*clientVendor=*/ "Goog",
78                 /*clientVersion=*/ "RCSAndrd-1.0");
79     }
80 
81     @Override
82     @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
triggerProvisioning()83     public void triggerProvisioning() throws ImsException {
84         boolean isRegistered = false;
85         synchronized (this) {
86             isRegistered = storedCallback.isPresent();
87         }
88 
89         if (isRegistered) {
90             triggerReconfiguration();
91         } else {
92             register();
93         }
94     }
95 
96     @Override
onConfigurationChange(ProvisioningStateChangeCallback cb)97     public void onConfigurationChange(ProvisioningStateChangeCallback cb) {
98         stateChangeCallback = Optional.of(cb);
99     }
100 
101     @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
register()102     public void register() throws ImsException {
103         register(getDefaultClientConfiguration());
104     }
105 
106     @SuppressWarnings("LogConditional")
107     // TODO(b/171976006) Use 'tools:ignore=' in manifest instead.
108     @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
register(@onNull RcsClientConfiguration clientConfiguration)109     public void register(@NonNull RcsClientConfiguration clientConfiguration) throws ImsException {
110         Log.i(TAG, "Using configuration: " + clientConfiguration.toString());
111         provisioningManager.setRcsClientConfiguration(clientConfiguration);
112 
113         RcsProvisioningCallback callback =
114                 new RcsProvisioningCallback() {
115                     @Override
116                     public void onConfigurationChanged(@NonNull byte[] configXml) {
117                         Log.i(TAG, "RcsProvisioningCallback.onConfigurationChanged called.");
118                         synchronized (this) {
119                             configXmlData = Optional.of(configXml);
120                         }
121                         stateChangeCallback.ifPresent(cb -> cb.notifyConfigChanged(configXml));
122                     }
123 
124                     @RequiresApi(api = VERSION_CODES.R)
125                     @Override
126                     public void onConfigurationReset() {
127                         Log.i(TAG, "RcsProvisioningCallback.onConfigurationReset called.");
128                         synchronized (this) {
129                             configXmlData = Optional.empty();
130                         }
131                         stateChangeCallback.ifPresent(cb -> cb.notifyConfigChanged(null));
132                     }
133 
134                     @RequiresApi(api = VERSION_CODES.R)
135                     @Override
136                     public void onRemoved() {
137                         Log.i(TAG, "RcsProvisioningCallback.onRemoved called.");
138                         synchronized (this) {
139                             configXmlData = Optional.empty();
140                         }
141                         stateChangeCallback.ifPresent(cb -> cb.notifyConfigChanged(null));
142                     }
143                 };
144 
145         Log.i(TAG, "Registering the callback.");
146         synchronized (this) {
147             provisioningManager.registerRcsProvisioningCallback(executorService, callback);
148             storedCallback = Optional.of(callback);
149         }
150     }
151 
152     @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
unRegister()153     public void unRegister() {
154         synchronized (this) {
155             RcsProvisioningCallback callback =
156                     storedCallback.orElseThrow(
157                             () -> new IllegalStateException("No callback present."));
158             provisioningManager.unregisterRcsProvisioningCallback(callback);
159             storedCallback = Optional.empty();
160         }
161     }
162 
163     @Override
164     @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
isRcsVolteSingleRegistrationCapable()165     public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
166         return provisioningManager.isRcsVolteSingleRegistrationCapable();
167     }
168 
getLatestConfiguration()169     public synchronized byte[] getLatestConfiguration() {
170         return configXmlData.orElseThrow(() -> new IllegalStateException("No config present"));
171     }
172 
173     @VisibleForTesting
174     @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
triggerReconfiguration()175     void triggerReconfiguration() {
176         provisioningManager.triggerRcsReconfiguration();
177     }
178 
179     @VisibleForTesting
getProvisioningManager()180     ProvisioningManager getProvisioningManager() {
181         return provisioningManager;
182     }
183 }
184