1 /*
2  * Copyright (C) 2022 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 android.telephony.cts.util;
18 
19 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
20 
21 import static org.junit.Assert.assertTrue;
22 
23 import android.annotation.NonNull;
24 import android.app.role.RoleManager;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.os.Process;
28 import android.os.UserHandle;
29 import android.telephony.TelephonyManager;
30 import android.text.TextUtils;
31 import android.util.Log;
32 
33 import androidx.test.core.app.ApplicationProvider;
34 
35 import com.android.compatibility.common.util.ShellIdentityUtils;
36 
37 import org.junit.Assume;
38 
39 import java.util.List;
40 import java.util.concurrent.CountDownLatch;
41 import java.util.concurrent.Executor;
42 import java.util.concurrent.LinkedBlockingQueue;
43 import java.util.concurrent.TimeUnit;
44 
45 public class DefaultSmsAppHelper {
46     private static final String TAG = "DefaultSmsAppHelper";
ensureDefaultSmsApp()47     public static void ensureDefaultSmsApp() {
48         if (!hasTelephony() || !hasSms()) {
49             return;
50         }
51 
52         Context context = ApplicationProvider.getApplicationContext();
53         String packageName = context.getPackageName();
54         RoleManager roleManager = context.getSystemService(RoleManager.class);
55         Executor executor = context.getMainExecutor();
56         UserHandle user = Process.myUserHandle();
57 
58         CountDownLatch latch = new CountDownLatch(1);
59         boolean[] success = new boolean[1];
60 
61         runWithShellPermissionIdentity(() -> {
62             roleManager.addRoleHolderAsUser(
63                     RoleManager.ROLE_SMS,
64                     packageName,
65                     RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP,
66                     user,
67                     executor,
68                     successful -> {
69                         success[0] = successful;
70                         latch.countDown();
71                     });
72         });
73 
74         try {
75             latch.await();
76             assertTrue(success[0]);
77         } catch (InterruptedException ex) {
78             throw new RuntimeException(ex.getMessage());
79         }
80     }
81 
stopBeingDefaultSmsApp()82     public static void stopBeingDefaultSmsApp() {
83         if (!hasSms()) {
84             return;
85         }
86         Context context = ApplicationProvider.getApplicationContext();
87         String packageName = context.getPackageName();
88         RoleManager roleManager = context.getSystemService(RoleManager.class);
89         Executor executor = context.getMainExecutor();
90         UserHandle user = Process.myUserHandle();
91 
92         CountDownLatch latch = new CountDownLatch(1);
93         boolean[] success = new boolean[1];
94 
95         runWithShellPermissionIdentity(() -> {
96             roleManager.removeRoleHolderAsUser(
97                     RoleManager.ROLE_SMS,
98                     packageName,
99                     RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP,
100                     user,
101                     executor,
102                     successful -> {
103                         success[0] = successful;
104                         latch.countDown();
105                     });
106         });
107 
108         try {
109             latch.await();
110             assertTrue(success[0]);
111         } catch (InterruptedException ex) {
112             throw new RuntimeException(ex.getMessage());
113         }
114     }
115 
116     /**
117      * Get the default SMS application configured on the device.
118      * @param context The context used for getting the default SMS application.
119      */
getDefaultSmsApp(@onNull Context context)120     public static String getDefaultSmsApp(@NonNull Context context) throws Exception {
121         RoleManager roleManager = context.getSystemService(RoleManager.class);
122         List<String> result = ShellIdentityUtils.invokeMethodWithShellPermissions(roleManager,
123                 (m) -> m.getRoleHolders(RoleManager.ROLE_SMS));
124         Log.d(TAG, "getDefaultSmsApp result: " + result);
125 
126         if (result.isEmpty()) {
127             // No default SMS app.
128             return null;
129         }
130         // There should only be one default sms app
131         return result.get(0);
132     }
133 
134     /**
135      * Set the default SMS application for the device.
136      * @param context The context used for setting the default SMS application.
137      * @param packageName The package name of the default SMS application to be set.
138      * @return {@code true} if success, {@code false} otherwise.
139      */
setDefaultSmsApp( @onNull Context context, String packageName)140     public static boolean setDefaultSmsApp(
141             @NonNull Context context, String packageName) throws Exception {
142         RoleManager roleManager = context.getSystemService(RoleManager.class);
143         Boolean result;
144         LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(1);
145         if (TextUtils.isEmpty(packageName)) {
146             ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(roleManager,
147                     (m) -> m.clearRoleHoldersAsUser(RoleManager.ROLE_SMS,
148                             RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP,
149                             android.os.Process.myUserHandle(),
150                             // Run on calling binder thread.
151                             Runnable::run, queue::offer));
152         } else {
153             ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(roleManager,
154                     (m) -> m.addRoleHolderAsUser(RoleManager.ROLE_SMS, packageName, 0,
155                             android.os.Process.myUserHandle(),
156                             // Run on calling binder thread.
157                             Runnable::run, queue::offer));
158         }
159         result = queue.poll(10, TimeUnit.SECONDS);
160         Log.d(TAG, "setDefaultSmsApp result: " + result);
161         return result;
162     }
163 
assumeTelephony()164     public static void assumeTelephony() {
165         Assume.assumeTrue(hasTelephony());
166     }
167 
assumeMessaging()168     public static void assumeMessaging() {
169         Assume.assumeTrue(hasSms());
170     }
171 
hasTelephony()172     public static boolean hasTelephony() {
173         Context context = ApplicationProvider.getApplicationContext();
174         return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
175     }
176 
hasSms()177     public static boolean hasSms() {
178         TelephonyManager telephonyManager = (TelephonyManager)
179                 ApplicationProvider.getApplicationContext().getSystemService(
180                         Context.TELEPHONY_SERVICE);
181         return telephonyManager.isSmsCapable();
182     }
183 }
184