• 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 android.telecom.CallerInfo;
37 
38 import java.util.List;
39 
40 /**
41  * Uses the incoming {@link Call}'s ringtone URI (obtained by the Contact Lookup) to obtain a
42  * {@link Ringtone} from the {@link RingtoneManager} that can be played by the system during an
43  * incoming call. If the ringtone URI is null, use the default Ringtone for the active user.
44  */
45 @VisibleForTesting
46 public class RingtoneFactory {
47 
48     private final Context mContext;
49     private final CallsManager mCallsManager;
50 
RingtoneFactory(CallsManager callsManager, Context context)51     public RingtoneFactory(CallsManager callsManager, Context context) {
52         mContext = context;
53         mCallsManager = callsManager;
54     }
55 
56     /**
57      * Determines if a ringtone has haptic channels.
58      * @param ringtone The ringtone URI.
59      * @return {@code true} if there is a haptic channel, {@code false} otherwise.
60      */
hasHapticChannels(Ringtone ringtone)61     public boolean hasHapticChannels(Ringtone ringtone) {
62         boolean hasHapticChannels = RingtoneManager.hasHapticChannels(ringtone.getUri());
63         Log.i(this, "hasHapticChannels %s -> %b", ringtone.getUri(), hasHapticChannels);
64         return hasHapticChannels;
65     }
66 
getRingtone(Call incomingCall, @Nullable VolumeShaper.Configuration volumeShaperConfig)67     public Ringtone getRingtone(Call incomingCall,
68             @Nullable VolumeShaper.Configuration volumeShaperConfig) {
69         // Use the default ringtone of the work profile if the contact is a work profile contact.
70         Context userContext = isWorkContact(incomingCall) ?
71                 getWorkProfileContextForUser(mCallsManager.getCurrentUserHandle()) :
72                 getContextForUserHandle(mCallsManager.getCurrentUserHandle());
73         Uri ringtoneUri = incomingCall.getRingtone();
74         Ringtone ringtone = null;
75 
76         AudioAttributes audioAttrs = getRingtoneAudioAttributes();
77 
78         if(ringtoneUri != null && userContext != null) {
79             // Ringtone URI is explicitly specified. First, try to create a Ringtone with that.
80             try {
81               ringtone = RingtoneManager.getRingtone(
82                   userContext, ringtoneUri, volumeShaperConfig, audioAttrs);
83             } catch (NullPointerException npe) {
84                 Log.e(this, npe, "getRingtone: NPE while getting ringtone.");
85             }
86         }
87         if(ringtone == null) {
88             // Contact didn't specify ringtone or custom Ringtone creation failed. Get default
89             // ringtone for user or profile.
90             Context contextToUse = hasDefaultRingtoneForUser(userContext) ? userContext : mContext;
91             Uri defaultRingtoneUri;
92             if (UserManager.get(contextToUse).isUserUnlocked(contextToUse.getUserId())) {
93                 defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(contextToUse,
94                         RingtoneManager.TYPE_RINGTONE);
95                 if (defaultRingtoneUri == null) {
96                     Log.i(this, "getRingtone: defaultRingtoneUri for user is null.");
97                 }
98             } else {
99                 defaultRingtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
100                 if (defaultRingtoneUri == null) {
101                     Log.i(this, "getRingtone: Settings.System.DEFAULT_RINGTONE_URI is null.");
102                 }
103             }
104             if (defaultRingtoneUri == null) {
105                 return null;
106             }
107             try {
108                 ringtone = RingtoneManager.getRingtone(
109                     contextToUse, defaultRingtoneUri, volumeShaperConfig, audioAttrs);
110             } catch (NullPointerException npe) {
111                 Log.e(this, npe, "getRingtone: NPE while getting ringtone.");
112             }
113         }
114         return ringtone;
115     }
116 
getRingtoneAudioAttributes()117     public AudioAttributes getRingtoneAudioAttributes() {
118         return new AudioAttributes.Builder()
119             .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
120             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
121             .build();
122     }
123 
getRingtone(Call incomingCall)124     public Ringtone getRingtone(Call incomingCall) {
125         return getRingtone(incomingCall, null);
126     }
127 
128     /** Returns a ringtone to be used when ringer is not audible for the incoming call. */
129     @Nullable
getHapticOnlyRingtone()130     public Ringtone getHapticOnlyRingtone() {
131         Uri ringtoneUri = Uri.parse("file://" + mContext.getString(
132                 com.android.internal.R.string.config_defaultRingtoneVibrationSound));
133         AudioAttributes audioAttrs = getRingtoneAudioAttributes();
134         Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri, null, audioAttrs);
135         if (ringtone != null) {
136             // Make sure the sound is muted.
137             ringtone.setVolume(0);
138         }
139         return ringtone;
140     }
141 
getWorkProfileContextForUser(UserHandle userHandle)142     private Context getWorkProfileContextForUser(UserHandle userHandle) {
143         // UserManager.getEnabledProfiles returns the enabled profiles along with the user's handle
144         // itself (so we must filter out the user).
145         List<UserInfo> profiles = UserManager.get(mContext).getEnabledProfiles(
146                 userHandle.getIdentifier());
147         UserInfo workprofile = null;
148         int managedProfileCount = 0;
149         for (UserInfo profile : profiles) {
150             UserHandle profileUserHandle = profile.getUserHandle();
151             if (profileUserHandle != userHandle && profile.isManagedProfile()) {
152                 managedProfileCount++;
153                 workprofile = profile;
154             }
155         }
156         // There may be many different types of profiles, so only count Managed (Work) Profiles.
157         if(managedProfileCount == 1) {
158             return getContextForUserHandle(workprofile.getUserHandle());
159         }
160         // There are multiple managed profiles for the associated user and we do not have enough
161         // info to determine which profile is the work profile. Just use the default.
162         return null;
163     }
164 
getContextForUserHandle(UserHandle userHandle)165     private Context getContextForUserHandle(UserHandle userHandle) {
166         if(userHandle == null) {
167             return null;
168         }
169         try {
170             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, userHandle);
171         } catch (PackageManager.NameNotFoundException e) {
172             Log.w("RingtoneFactory", "Package name not found: " + e.getMessage());
173         }
174         return null;
175     }
176 
hasDefaultRingtoneForUser(Context userContext)177     private boolean hasDefaultRingtoneForUser(Context userContext) {
178         if(userContext == null) {
179             return false;
180         }
181         return !TextUtils.isEmpty(Settings.System.getStringForUser(userContext.getContentResolver(),
182                 Settings.System.RINGTONE, userContext.getUserId()));
183     }
184 
isWorkContact(Call incomingCall)185     private boolean isWorkContact(Call incomingCall) {
186         CallerInfo contactCallerInfo = incomingCall.getCallerInfo();
187         return (contactCallerInfo != null) &&
188                 (contactCallerInfo.userType == CallerInfo.USER_TYPE_WORK);
189     }
190 }
191