• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.server.telecom;
18 
19 import android.annotation.Nullable;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.content.pm.UserInfo;
23 import android.media.AudioAttributes;
24 import android.media.RingtoneManager;
25 import android.media.Ringtone;
26 import android.media.VolumeShaper;
27 import android.net.Uri;
28 import android.os.UserHandle;
29 import android.os.UserManager;
30 import android.provider.Settings;
31 
32 import android.telecom.Log;
33 import android.text.TextUtils;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.server.telecom.flags.FeatureFlags;
37 
38 import android.telecom.CallerInfo;
39 import android.util.Pair;
40 
41 import java.util.List;
42 
43 /**
44  * Uses the incoming {@link Call}'s ringtone URI (obtained by the Contact Lookup) to obtain a
45  * {@link Ringtone} from the {@link RingtoneManager} that can be played by the system during an
46  * incoming call. If the ringtone URI is null, use the default Ringtone for the active user.
47  */
48 @VisibleForTesting
49 public class RingtoneFactory {
50 
51     private final Context mContext;
52     private final CallsManager mCallsManager;
53     private FeatureFlags mFeatureFlags;
54 
RingtoneFactory(CallsManager callsManager, Context context, FeatureFlags featureFlags)55     public RingtoneFactory(CallsManager callsManager, Context context, FeatureFlags featureFlags) {
56         mContext = context;
57         mCallsManager = callsManager;
58         mFeatureFlags = featureFlags;
59     }
60 
getRingtone(Call incomingCall, @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean hapticChannelsMuted)61     public Pair<Uri, Ringtone> getRingtone(Call incomingCall,
62             @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean hapticChannelsMuted) {
63         // Initializing ringtones on the main thread can deadlock
64         ThreadUtil.checkNotOnMainThread();
65 
66         AudioAttributes audioAttrs = getDefaultRingtoneAudioAttributes(hapticChannelsMuted);
67 
68         // Use the default ringtone of the work profile if the contact is a work profile contact.
69         // or the default ringtone of the receiving user.
70         Context userContext = isWorkContact(incomingCall) ?
71                 getWorkProfileContextForUser(mCallsManager.getCurrentUserHandle()) :
72                 getContextForUserHandle(incomingCall.getAssociatedUser());
73         Uri ringtoneUri = incomingCall.getRingtone();
74         Ringtone ringtone = null;
75 
76         if (ringtoneUri != null && userContext != null) {
77             // Ringtone URI is explicitly specified. First, try to create a Ringtone with that.
78             try {
79                 ringtone = RingtoneManager.getRingtone(
80                         userContext, ringtoneUri, volumeShaperConfig, audioAttrs);
81             } catch (Exception e) {
82                 Log.e(this, e, "getRingtone: exception while getting ringtone.");
83             }
84         }
85         if (ringtone == null) {
86             // Contact didn't specify ringtone or custom Ringtone creation failed. Get default
87             // ringtone for user or profile.
88             Context contextToUse = hasDefaultRingtoneForUser(userContext) ? userContext : mContext;
89             UserManager um = contextToUse.getSystemService(UserManager.class);
90             boolean isUserUnlocked = mFeatureFlags.telecomResolveHiddenDependencies()
91                     ? um.isUserUnlocked(contextToUse.getUser())
92                     : um.isUserUnlocked(contextToUse.getUserId());
93             Uri defaultRingtoneUri;
94             if (isUserUnlocked) {
95                 defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(contextToUse,
96                         RingtoneManager.TYPE_RINGTONE);
97                 if (defaultRingtoneUri == null) {
98                     Log.i(this, "getRingtone: defaultRingtoneUri for user is null.");
99                 }
100             } else {
101                 defaultRingtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
102                 if (defaultRingtoneUri == null) {
103                     Log.i(this, "getRingtone: Settings.System.DEFAULT_RINGTONE_URI is null.");
104                 }
105             }
106 
107             ringtoneUri = defaultRingtoneUri;
108             if (ringtoneUri == null) {
109                 return null;
110             }
111 
112             try {
113                 ringtone = RingtoneManager.getRingtone(
114                         contextToUse, ringtoneUri, volumeShaperConfig, audioAttrs);
115             } catch (Exception e) {
116                 Log.e(this, e, "getRingtone: exception while getting ringtone.");
117             }
118         }
119         return new Pair(ringtoneUri, ringtone);
120     }
121 
getDefaultRingtoneAudioAttributes(boolean hapticChannelsMuted)122     private AudioAttributes getDefaultRingtoneAudioAttributes(boolean hapticChannelsMuted) {
123         return new AudioAttributes.Builder()
124             .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
125             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
126             .setHapticChannelsMuted(hapticChannelsMuted)
127             .build();
128     }
129 
getWorkProfileContextForUser(UserHandle userHandle)130     private Context getWorkProfileContextForUser(UserHandle userHandle) {
131         // UserManager.getUserProfiles returns the enabled profiles along with the context user's
132         // handle itself (so we must filter out the user).
133         Context userContext = mContext.createContextAsUser(userHandle, 0);
134         UserManager um = mFeatureFlags.telecomResolveHiddenDependencies()
135                 ? userContext.getSystemService(UserManager.class)
136                 : mContext.getSystemService(UserManager.class);
137         List<UserHandle> profiles = um.getUserProfiles();
138         List<UserInfo> userInfoProfiles = um.getEnabledProfiles(userHandle.getIdentifier());
139         UserHandle workProfileUser = null;
140         int managedProfileCount = 0;
141 
142         if (mFeatureFlags.telecomResolveHiddenDependencies()) {
143             for (UserHandle profileUser : profiles) {
144                 UserManager userManager = mContext.createContextAsUser(profileUser, 0)
145                         .getSystemService(UserManager.class);
146                 if (!userHandle.equals(profileUser) && userManager.isManagedProfile()) {
147                     managedProfileCount++;
148                     workProfileUser = profileUser;
149                 }
150             }
151         } else {
152             for(UserInfo profile: userInfoProfiles) {
153                 UserHandle profileUserHandle = profile.getUserHandle();
154                 if (!profileUserHandle.equals(userHandle) && profile.isManagedProfile()) {
155                     managedProfileCount++;
156                     workProfileUser = profileUserHandle;
157                 }
158             }
159         }
160         // There may be many different types of profiles, so only count Managed (Work) Profiles.
161         if(managedProfileCount == 1) {
162             return getContextForUserHandle(workProfileUser);
163         }
164         // There are multiple managed profiles for the associated user and we do not have enough
165         // info to determine which profile is the work profile. Just use the default.
166         return null;
167     }
168 
getContextForUserHandle(UserHandle userHandle)169     private Context getContextForUserHandle(UserHandle userHandle) {
170         if(userHandle == null) {
171             return null;
172         }
173         try {
174             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, userHandle);
175         } catch (PackageManager.NameNotFoundException e) {
176             Log.w("RingtoneFactory", "Package name not found: " + e.getMessage());
177         }
178         return null;
179     }
180 
hasDefaultRingtoneForUser(Context userContext)181     private boolean hasDefaultRingtoneForUser(Context userContext) {
182         if(userContext == null) {
183             return false;
184         }
185         return !TextUtils.isEmpty(Settings.System.getStringForUser(userContext.getContentResolver(),
186                 Settings.System.RINGTONE, userContext.getUserId()));
187     }
188 
isWorkContact(Call incomingCall)189     private boolean isWorkContact(Call incomingCall) {
190         CallerInfo contactCallerInfo = incomingCall.getCallerInfo();
191         return (contactCallerInfo != null) &&
192                 (contactCallerInfo.userType == CallerInfo.USER_TYPE_WORK);
193     }
194 }
195