• 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         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(userContext, ringtoneUri,
80                         volumeShaperConfig);
81             } catch (NullPointerException npe) {
82                 Log.e(this, npe, "getRingtone: NPE 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             Uri defaultRingtoneUri;
90             if (UserManager.get(contextToUse).isUserUnlocked(contextToUse.getUserId())) {
91                 defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(contextToUse,
92                         RingtoneManager.TYPE_RINGTONE);
93             } else {
94                 defaultRingtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
95             }
96             if (defaultRingtoneUri == null) {
97                 return null;
98             }
99             try {
100                 ringtone = RingtoneManager.getRingtone(
101                         contextToUse, defaultRingtoneUri, volumeShaperConfig);
102             } catch (NullPointerException npe) {
103                 Log.e(this, npe, "getRingtone: NPE while getting ringtone.");
104             }
105         }
106         if (ringtone != null) {
107             ringtone.setAudioAttributes(new AudioAttributes.Builder()
108                     .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
109                     .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
110                     .build());
111         }
112         return ringtone;
113     }
114 
getRingtone(Call incomingCall)115     public Ringtone getRingtone(Call incomingCall) {
116         return getRingtone(incomingCall, null);
117     }
118 
getWorkProfileContextForUser(UserHandle userHandle)119     private Context getWorkProfileContextForUser(UserHandle userHandle) {
120         // UserManager.getEnabledProfiles returns the enabled profiles along with the user's handle
121         // itself (so we must filter out the user).
122         List<UserInfo> profiles = UserManager.get(mContext).getEnabledProfiles(
123                 userHandle.getIdentifier());
124         UserInfo workprofile = null;
125         int managedProfileCount = 0;
126         for (UserInfo profile : profiles) {
127             UserHandle profileUserHandle = profile.getUserHandle();
128             if (profileUserHandle != userHandle && profile.isManagedProfile()) {
129                 managedProfileCount++;
130                 workprofile = profile;
131             }
132         }
133         // There may be many different types of profiles, so only count Managed (Work) Profiles.
134         if(managedProfileCount == 1) {
135             return getContextForUserHandle(workprofile.getUserHandle());
136         }
137         // There are multiple managed profiles for the associated user and we do not have enough
138         // info to determine which profile is the work profile. Just use the default.
139         return null;
140     }
141 
getContextForUserHandle(UserHandle userHandle)142     private Context getContextForUserHandle(UserHandle userHandle) {
143         if(userHandle == null) {
144             return null;
145         }
146         try {
147             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, userHandle);
148         } catch (PackageManager.NameNotFoundException e) {
149             Log.w("RingtoneFactory", "Package name not found: " + e.getMessage());
150         }
151         return null;
152     }
153 
hasDefaultRingtoneForUser(Context userContext)154     private boolean hasDefaultRingtoneForUser(Context userContext) {
155         if(userContext == null) {
156             return false;
157         }
158         return !TextUtils.isEmpty(Settings.System.getStringForUser(userContext.getContentResolver(),
159                 Settings.System.RINGTONE, userContext.getUserId()));
160     }
161 
isWorkContact(Call incomingCall)162     private boolean isWorkContact(Call incomingCall) {
163         CallerInfo contactCallerInfo = incomingCall.getCallerInfo();
164         return (contactCallerInfo != null) &&
165                 (contactCallerInfo.userType == CallerInfo.USER_TYPE_WORK);
166     }
167 }
168