• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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