1 /* 2 * Copyright (C) 2014 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.providers.telephony; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.ActivityManager; 22 import android.content.ComponentName; 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.net.Uri; 27 import android.os.Process; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.provider.Telephony; 31 import android.telephony.SubscriptionInfo; 32 import android.telephony.SubscriptionManager; 33 import android.telephony.TelephonyManager; 34 import android.telephony.emergency.EmergencyNumber; 35 import android.text.TextUtils; 36 import android.util.Log; 37 38 import com.android.internal.telephony.SmsApplication; 39 import com.android.internal.telephony.TelephonyPermissions; 40 import com.android.internal.telephony.flags.Flags; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.stream.Collectors; 47 48 /** 49 * Helpers 50 */ 51 public class ProviderUtil { 52 private final static String TAG = "SmsProvider"; 53 private static final String TELEPHONY_PROVIDER_PACKAGE = "com.android.providers.telephony"; 54 55 /** 56 * Check if a caller of the provider has restricted access, 57 * i.e. being non-system, non-phone, non-default SMS app 58 * 59 * @param context the context to use 60 * @param packageName the caller package name 61 * @param uid the caller uid 62 * @return true if the caller is not system, or phone or default sms app, false otherwise 63 */ isAccessRestricted(Context context, String packageName, int uid)64 public static boolean isAccessRestricted(Context context, String packageName, int uid) { 65 return (!TelephonyPermissions.isSystemOrPhone(uid) 66 && !SmsApplication.isDefaultSmsApplication(context, packageName)); 67 } 68 69 /** 70 * Whether should set CREATOR for an insertion 71 * 72 * @param values The content of the message 73 * @param uid The caller UID of the insertion 74 * @return true if we should set CREATOR, false otherwise 75 */ shouldSetCreator(ContentValues values, int uid)76 public static boolean shouldSetCreator(ContentValues values, int uid) { 77 return (!TelephonyPermissions.isSystemOrPhone(uid)) 78 || (!values.containsKey(Telephony.Sms.CREATOR) 79 && !values.containsKey(Telephony.Mms.CREATOR)); 80 } 81 82 /** 83 * Whether should remove CREATOR for an update 84 * 85 * @param values The content of the message 86 * @param uid The caller UID of the update 87 * @return true if we should remove CREATOR, false otherwise 88 */ shouldRemoveCreator(ContentValues values, int uid)89 public static boolean shouldRemoveCreator(ContentValues values, int uid) { 90 return (!TelephonyPermissions.isSystemOrPhone(uid)) 91 && (values.containsKey(Telephony.Sms.CREATOR) 92 || values.containsKey(Telephony.Mms.CREATOR)); 93 } 94 95 /** 96 * Notify the default SMS app of an SMS/MMS provider change if the change is being made 97 * by a package other than the default SMS app itself. 98 * 99 * @param uri The uri the provider change applies to 100 * @param callingPackage The package name of the provider caller 101 * @param Context 102 */ notifyIfNotDefaultSmsApp(final Uri uri, final String callingPackage, final Context context)103 public static void notifyIfNotDefaultSmsApp(final Uri uri, final String callingPackage, 104 final Context context) { 105 if (TextUtils.equals(callingPackage, Telephony.Sms.getDefaultSmsPackage(context))) { 106 if (Log.isLoggable(TAG, Log.VERBOSE)) { 107 Log.d(TAG, "notifyIfNotDefaultSmsApp - called from default sms app"); 108 } 109 return; 110 } 111 // Direct the intent to only the default SMS app, and only if the SMS app has a receiver 112 // for the intent. 113 ComponentName componentName = 114 SmsApplication.getDefaultExternalTelephonyProviderChangedApplication(context, true); 115 if (componentName == null) { 116 return; // the default sms app doesn't have a receiver for this intent 117 } 118 119 final Intent intent = 120 new Intent(Telephony.Sms.Intents.ACTION_EXTERNAL_PROVIDER_CHANGE); 121 intent.setFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 122 intent.setComponent(componentName); 123 if (uri != null) { 124 intent.setData(uri); 125 } 126 if (Log.isLoggable(TAG, Log.VERBOSE)) { 127 Log.d(TAG, "notifyIfNotDefaultSmsApp - called from " + callingPackage + ", notifying"); 128 } 129 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 130 context.sendBroadcast(intent); 131 } 132 getCredentialEncryptedContext(Context context)133 public static Context getCredentialEncryptedContext(Context context) { 134 if (context.isCredentialProtectedStorage()) { 135 return context; 136 } 137 return context.createCredentialProtectedStorageContext(); 138 } 139 getDeviceEncryptedContext(Context context)140 public static Context getDeviceEncryptedContext(Context context) { 141 if (context.isDeviceProtectedStorage()) { 142 return context; 143 } 144 return context.createDeviceProtectedStorageContext(); 145 } 146 147 /** 148 * Get subscriptions associated with the user in the format of a selection string. 149 * @param context context 150 * @param userHandle caller user handle. 151 * @return subscriptions associated with the user in the format of a selection string 152 * or {@code null} if user is not associated with any subscription. 153 */ 154 @Nullable getSelectionBySubIds(Context context, @NonNull final UserHandle userHandle)155 public static String getSelectionBySubIds(Context context, 156 @NonNull final UserHandle userHandle) { 157 List<SubscriptionInfo> associatedSubscriptionsList = new ArrayList<>(); 158 SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class); 159 UserManager userManager = context.getSystemService(UserManager.class); 160 161 if (Flags.workProfileApiSplit()) { 162 if (subManager != null) { 163 // Get list of subscriptions accessible to this user. 164 associatedSubscriptionsList = subManager 165 .getSubscriptionInfoListAssociatedWithUser(userHandle); 166 167 if ((userManager != null) 168 && userManager.isManagedProfile(userHandle.getIdentifier())) { 169 // Work profile caller can only see subscriptions explicitly associated with it. 170 associatedSubscriptionsList = associatedSubscriptionsList.stream() 171 .filter(info -> userHandle.equals(subManager 172 .getSubscriptionUserHandle(info.getSubscriptionId()))) 173 .collect(Collectors.toList()); 174 } else { 175 // SMS/MMS restored from another device have sub_id=-1. 176 // To query/update/delete those messages, sub_id=-1 should be in the selection 177 // string. 178 SubscriptionInfo invalidSubInfo = new SubscriptionInfo.Builder() 179 .setId(SubscriptionManager.INVALID_SUBSCRIPTION_ID) 180 .build(); 181 associatedSubscriptionsList.add(invalidSubInfo); 182 } 183 } 184 } else { 185 if (subManager != null) { 186 // Get list of subscriptions associated with this user. 187 associatedSubscriptionsList = subManager 188 .getSubscriptionInfoListAssociatedWithUser(userHandle); 189 } 190 191 if ((userManager != null) 192 && (!userManager.isManagedProfile(userHandle.getIdentifier()))) { 193 // SMS/MMS restored from another device have sub_id=-1. 194 // To query/update/delete those messages, sub_id=-1 should be in the selection 195 // string. 196 SubscriptionInfo invalidSubInfo = new SubscriptionInfo.Builder() 197 .setId(SubscriptionManager.INVALID_SUBSCRIPTION_ID) 198 .build(); 199 associatedSubscriptionsList.add(invalidSubInfo); 200 } 201 } 202 203 if (associatedSubscriptionsList.isEmpty()) { 204 return null; 205 } 206 207 // Converts [1,2,3,4,-1] to "'1','2','3','4','-1'" so that it can be appended to 208 // selection string 209 String subIdListStr = associatedSubscriptionsList.stream() 210 .map(subInfo -> ("'" + subInfo.getSubscriptionId() + "'")) 211 .collect(Collectors.joining(",")); 212 String selectionBySubId = (Telephony.Sms.SUBSCRIPTION_ID + " IN (" + subIdListStr + ")"); 213 if (Log.isLoggable(TAG, Log.VERBOSE)) { 214 Log.d(TAG, "getSelectionBySubIds: " + selectionBySubId); 215 } 216 return selectionBySubId; 217 } 218 219 /** 220 * Get emergency number list in the format of a selection string. 221 * @param context context 222 * @return emergency number list in the format of a selection string 223 * or {@code null} if emergency number list is empty. 224 */ 225 @Nullable getSelectionByEmergencyNumbers(@onNull Context context)226 public static String getSelectionByEmergencyNumbers(@NonNull Context context) { 227 // Get emergency number list to add it to selection string. 228 TelephonyManager tm = context.getSystemService(TelephonyManager.class); 229 Map<Integer, List<EmergencyNumber>> emergencyNumberList = null; 230 try { 231 if (tm != null) { 232 emergencyNumberList = tm.getEmergencyNumberList(); 233 } 234 } catch (Exception e) { 235 Log.e(TAG, "Cannot get emergency number list", e); 236 } 237 238 String selectionByEmergencyNumber = null; 239 if (emergencyNumberList != null && !emergencyNumberList.isEmpty()) { 240 String emergencyNumberListStr = ""; 241 for (Map.Entry<Integer, List<EmergencyNumber>> entry : emergencyNumberList.entrySet()) { 242 if (!emergencyNumberListStr.isEmpty() && !entry.getValue().isEmpty()) { 243 emergencyNumberListStr += ','; 244 } 245 246 emergencyNumberListStr += entry.getValue().stream() 247 .map(emergencyNumber -> ("'" + emergencyNumber.getNumber() + "'")) 248 .collect(Collectors.joining(",")); 249 } 250 selectionByEmergencyNumber = Telephony.Sms.ADDRESS + 251 " IN (" + emergencyNumberListStr + ")"; 252 } 253 return selectionByEmergencyNumber; 254 } 255 256 /** 257 * Check sub is either default value(for backup restore) or is accessible by the caller profile. 258 * @param ctx Context 259 * @param subId The sub Id associated with the entry 260 * @param callerUserHandle The user handle of the caller profile 261 * @return {@code true} if allow the caller to insert an entry that's associated with this sub. 262 */ allowInteractingWithEntryOfSubscription(Context ctx, int subId, UserHandle callerUserHandle)263 public static boolean allowInteractingWithEntryOfSubscription(Context ctx, 264 int subId, UserHandle callerUserHandle) { 265 return TelephonyPermissions 266 .checkSubscriptionAssociatedWithUser(ctx, subId, callerUserHandle) 267 // INVALID_SUBSCRIPTION_ID represents backup restore. 268 || subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID; 269 } 270 271 /** 272 * Log all running processes of the telephony provider package. 273 */ logRunningTelephonyProviderProcesses(@onNull Context context)274 public static void logRunningTelephonyProviderProcesses(@NonNull Context context) { 275 ActivityManager am = context.getSystemService(ActivityManager.class); 276 if (am == null) { 277 Log.d(TAG, "logRunningTelephonyProviderProcesses: ActivityManager service is not" 278 + " available"); 279 return; 280 } 281 282 List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses(); 283 if (processInfos == null) { 284 Log.d(TAG, "logRunningTelephonyProviderProcesses: processInfos is null"); 285 return; 286 } 287 288 StringBuilder sb = new StringBuilder(); 289 for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) { 290 if (Arrays.asList(processInfo.pkgList).contains(TELEPHONY_PROVIDER_PACKAGE) 291 || UserHandle.isSameApp(processInfo.uid, Process.PHONE_UID)) { 292 sb.append("{ProcessName="); 293 sb.append(processInfo.processName); 294 sb.append(";PID="); 295 sb.append(processInfo.pid); 296 sb.append(";UID="); 297 sb.append(processInfo.uid); 298 sb.append(";pkgList="); 299 for (String pkg : processInfo.pkgList) { 300 sb.append(pkg + ";"); 301 } 302 sb.append("}"); 303 } 304 } 305 Log.d(TAG, "RunningTelephonyProviderProcesses:" + sb.toString()); 306 } 307 } 308