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.Executor; 41 import java.util.concurrent.LinkedBlockingQueue; 42 import java.util.concurrent.TimeUnit; 43 44 public class DefaultSmsAppHelper { 45 private static final String TAG = "DefaultSmsAppHelper"; 46 private static final int ASYNC_TIMEOUT = 10000; ensureDefaultSmsApp()47 public static void ensureDefaultSmsApp() { 48 Log.d(TAG, "ensureDefaultSmsApp"); 49 if (!hasTelephony() || !hasSms()) { 50 Log.d(TAG, "ensureDefaultSmsApp: does not have telephony or sms feature."); 51 return; 52 } 53 54 Context context = ApplicationProvider.getApplicationContext(); 55 String packageName = context.getPackageName(); 56 RoleManager roleManager = context.getSystemService(RoleManager.class); 57 Executor executor = context.getMainExecutor(); 58 UserHandle user = Process.myUserHandle(); 59 LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(1); 60 Log.d(TAG, "ensureDefaultSmsApp: user=" + user.getIdentifier() 61 + " packageName=" + packageName); 62 63 runWithShellPermissionIdentity(() -> { 64 roleManager.addRoleHolderAsUser( 65 RoleManager.ROLE_SMS, 66 packageName, 67 RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, 68 user, 69 executor, 70 successful -> { 71 Log.d(TAG, "ensureDefaultSmsApp: successful=" + successful); 72 try { 73 queue.put(successful); 74 } catch (InterruptedException e) { 75 e.printStackTrace(); 76 throw new RuntimeException(e.getMessage()); 77 } 78 }); 79 }); 80 81 boolean result; 82 try { 83 result = queue.poll(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS); 84 } catch (InterruptedException e) { 85 e.printStackTrace(); 86 throw new RuntimeException(e.getMessage()); 87 } 88 89 Log.d(TAG, "ensureDefaultSmsApp: result=" + result); 90 assertTrue(result); 91 } 92 stopBeingDefaultSmsApp()93 public static void stopBeingDefaultSmsApp() { 94 Log.d(TAG, "stopBeingDefaultSmsApp"); 95 if (!hasSms()) { 96 Log.d(TAG, "stopBeingDefaultSmsApp: does not have sms feature."); 97 return; 98 } 99 100 Context context = ApplicationProvider.getApplicationContext(); 101 String packageName = context.getPackageName(); 102 RoleManager roleManager = context.getSystemService(RoleManager.class); 103 Executor executor = context.getMainExecutor(); 104 UserHandle user = Process.myUserHandle(); 105 LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(1); 106 Log.d(TAG, "stopBeingDefaultSmsApp: user=" + user.getIdentifier() 107 + " packageName=" + packageName); 108 109 runWithShellPermissionIdentity(() -> { 110 roleManager.removeRoleHolderAsUser( 111 RoleManager.ROLE_SMS, 112 packageName, 113 RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, 114 user, 115 executor, 116 successful -> { 117 Log.d(TAG, "stopBeingDefaultSmsApp: successful=" + successful); 118 try { 119 queue.put(successful); 120 } catch (InterruptedException e) { 121 e.printStackTrace(); 122 throw new RuntimeException(e.getMessage()); 123 } 124 }); 125 }); 126 127 boolean result; 128 try { 129 result = queue.poll(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS); 130 } catch (InterruptedException e) { 131 e.printStackTrace(); 132 throw new RuntimeException(e.getMessage()); 133 } 134 135 Log.d(TAG, "stopBeingDefaultSmsApp: result=" + result); 136 assertTrue(result); 137 } 138 139 /** 140 * Get the default SMS application configured on the device. 141 * @param context The context used for getting the default SMS application. 142 */ getDefaultSmsApp(@onNull Context context)143 public static String getDefaultSmsApp(@NonNull Context context) throws Exception { 144 RoleManager roleManager = context.getSystemService(RoleManager.class); 145 List<String> result = ShellIdentityUtils.invokeMethodWithShellPermissions(roleManager, 146 (m) -> m.getRoleHolders(RoleManager.ROLE_SMS)); 147 Log.d(TAG, "getDefaultSmsApp result: " + result); 148 149 if (result.isEmpty()) { 150 // No default SMS app. 151 return null; 152 } 153 // There should only be one default sms app 154 return result.get(0); 155 } 156 157 /** 158 * Set the default SMS application for the device. 159 * @param context The context used for setting the default SMS application. 160 * @param packageName The package name of the default SMS application to be set. 161 * @return {@code true} if success, {@code false} otherwise. 162 */ setDefaultSmsApp( @onNull Context context, String packageName)163 public static boolean setDefaultSmsApp( 164 @NonNull Context context, String packageName) throws Exception { 165 Log.d(TAG, "setDefaultSmsApp: packageName:" + packageName); 166 RoleManager roleManager = context.getSystemService(RoleManager.class); 167 Boolean result; 168 LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(1); 169 if (TextUtils.isEmpty(packageName)) { 170 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(roleManager, 171 (m) -> m.clearRoleHoldersAsUser(RoleManager.ROLE_SMS, 172 RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, 173 android.os.Process.myUserHandle(), 174 // Run on calling binder thread. 175 Runnable::run, queue::offer)); 176 } else { 177 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(roleManager, 178 (m) -> m.addRoleHolderAsUser(RoleManager.ROLE_SMS, packageName, 0, 179 android.os.Process.myUserHandle(), 180 // Run on calling binder thread. 181 Runnable::run, queue::offer)); 182 } 183 result = queue.poll(10, TimeUnit.SECONDS); 184 Log.d(TAG, "setDefaultSmsApp result: " + result); 185 return result; 186 } 187 assumeTelephony()188 public static void assumeTelephony() { 189 Assume.assumeTrue(hasTelephony()); 190 } 191 assumeMessaging()192 public static void assumeMessaging() { 193 Assume.assumeTrue(hasSms()); 194 } 195 hasTelephony()196 public static boolean hasTelephony() { 197 Context context = ApplicationProvider.getApplicationContext(); 198 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); 199 } 200 hasSms()201 public static boolean hasSms() { 202 TelephonyManager telephonyManager = (TelephonyManager) 203 ApplicationProvider.getApplicationContext().getSystemService( 204 Context.TELEPHONY_SERVICE); 205 return telephonyManager.isSmsCapable(); 206 } 207 } 208