1 /*
2  * Copyright (C) 2016 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.app.ActivityManager;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.database.ContentObserver;
25 import android.net.Uri;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.os.UserHandle;
29 import android.provider.Settings;
30 import android.telecom.DefaultDialerManager;
31 import android.telecom.Log;
32 import android.util.SparseArray;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.internal.util.IndentingPrintWriter;
36 
37 import java.util.Objects;
38 
39 public class DefaultDialerCache {
40     public interface DefaultDialerManagerAdapter {
getDefaultDialerApplication(Context context)41         String getDefaultDialerApplication(Context context);
getDefaultDialerApplication(Context context, int userId)42         String getDefaultDialerApplication(Context context, int userId);
setDefaultDialerApplication(Context context, String packageName, int userId)43         boolean setDefaultDialerApplication(Context context, String packageName, int userId);
44     }
45 
46     static class DefaultDialerManagerAdapterImpl implements DefaultDialerManagerAdapter {
47         @Override
getDefaultDialerApplication(Context context)48         public String getDefaultDialerApplication(Context context) {
49             return DefaultDialerManager.getDefaultDialerApplication(context);
50         }
51 
52         @Override
getDefaultDialerApplication(Context context, int userId)53         public String getDefaultDialerApplication(Context context, int userId) {
54             return DefaultDialerManager.getDefaultDialerApplication(context, userId);
55         }
56 
57         @Override
setDefaultDialerApplication(Context context, String packageName, int userId)58         public boolean setDefaultDialerApplication(Context context, String packageName,
59                 int userId) {
60             return DefaultDialerManager.setDefaultDialerApplication(context, packageName, userId);
61         }
62     }
63 
64     private static final String LOG_TAG = "DefaultDialerCache";
65     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
66         @Override
67         public void onReceive(Context context, Intent intent) {
68             Log.startSession("DDC.oR");
69             try {
70                 String packageName;
71                 if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
72                     packageName = null;
73                 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
74                         && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
75                     packageName = intent.getData().getSchemeSpecificPart();
76                 } else if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
77                     packageName = null;
78                 } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
79                     packageName = null;
80                 } else {
81                     return;
82                 }
83 
84                 synchronized (mLock) {
85                     refreshCachesForUsersWithPackage(packageName);
86                 }
87 
88             } finally {
89                 Log.endSession();
90             }
91         }
92     };
93 
94     private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
95         @Override
96         public void onReceive(Context context, Intent intent) {
97             if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
98                 int removedUser = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
99                     UserHandle.USER_NULL);
100                 if (removedUser == UserHandle.USER_NULL) {
101                     Log.w(LOG_TAG, "Expected EXTRA_USER_HANDLE with ACTION_USER_REMOVED");
102                 } else {
103                     removeUserFromCache(removedUser);
104                     Log.i(LOG_TAG, "Removing user %s", removedUser);
105                 }
106             }
107         }
108     };
109 
110     private final Handler mHandler = new Handler(Looper.getMainLooper());
111     private final ContentObserver mDefaultDialerObserver = new ContentObserver(mHandler) {
112         @Override
113         public void onChange(boolean selfChange) {
114             Log.startSession("DDC.oC");
115             try {
116                 // We don't get the user ID of the user that changed here, so we'll have to
117                 // refresh all of the users.
118                 synchronized (mLock) {
119                     refreshCachesForUsersWithPackage(null);
120                 }
121             } finally {
122                 Log.endSession();
123             }
124         }
125 
126         @Override
127         public boolean deliverSelfNotifications() {
128             return true;
129         }
130     };
131 
132     private final Context mContext;
133     private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
134     private final TelecomSystem.SyncRoot mLock;
135     private final String mSystemDialerName;
136     private SparseArray<String> mCurrentDefaultDialerPerUser = new SparseArray<>();
137 
DefaultDialerCache(Context context, DefaultDialerManagerAdapter defaultDialerManagerAdapter, TelecomSystem.SyncRoot lock)138     public DefaultDialerCache(Context context,
139             DefaultDialerManagerAdapter defaultDialerManagerAdapter,
140             TelecomSystem.SyncRoot lock) {
141         mContext = context;
142         mDefaultDialerManagerAdapter = defaultDialerManagerAdapter;
143         mLock = lock;
144         mSystemDialerName = mContext.getResources().getString(R.string.ui_default_package);
145 
146         IntentFilter packageIntentFilter = new IntentFilter();
147         packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
148         packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
149         packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
150         packageIntentFilter.addDataScheme("package");
151         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, packageIntentFilter, null, null);
152 
153         IntentFilter bootIntentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
154         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, bootIntentFilter, null, null);
155 
156         IntentFilter userRemovedFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
157         context.registerReceiver(mUserRemovedReceiver, userRemovedFilter);
158 
159         Uri defaultDialerSetting =
160                 Settings.Secure.getUriFor(Settings.Secure.DIALER_DEFAULT_APPLICATION);
161         context.getContentResolver()
162                 .registerContentObserver(defaultDialerSetting, false, mDefaultDialerObserver,
163                         UserHandle.USER_ALL);
164     }
165 
getDefaultDialerApplication(int userId)166     public String getDefaultDialerApplication(int userId) {
167         if (userId == UserHandle.USER_CURRENT) {
168             userId = ActivityManager.getCurrentUser();
169         }
170 
171         if (userId < 0) {
172             Log.w(LOG_TAG, "Attempting to get default dialer for a meta-user %d", userId);
173             return null;
174         }
175 
176         synchronized (mLock) {
177             String defaultDialer = mCurrentDefaultDialerPerUser.get(userId);
178             if (defaultDialer != null) {
179                 return defaultDialer;
180             }
181         }
182         return refreshCacheForUser(userId);
183     }
184 
getDefaultDialerApplication()185     public String getDefaultDialerApplication() {
186         return getDefaultDialerApplication(mContext.getUserId());
187     }
188 
isDefaultOrSystemDialer(String packageName, int userId)189     public boolean isDefaultOrSystemDialer(String packageName, int userId) {
190         String defaultDialer = getDefaultDialerApplication(userId);
191         return Objects.equals(packageName, defaultDialer)
192                 || Objects.equals(packageName, mSystemDialerName);
193     }
194 
setDefaultDialer(String packageName, int userId)195     public boolean setDefaultDialer(String packageName, int userId) {
196         boolean isChanged = mDefaultDialerManagerAdapter.setDefaultDialerApplication(
197                 mContext, packageName, userId);
198         if(isChanged) {
199             synchronized (mLock) {
200                 // Update the cache synchronously so that there is no delay in cache update.
201                 mCurrentDefaultDialerPerUser.put(userId, packageName);
202             }
203         }
204         return isChanged;
205     }
206 
refreshCacheForUser(int userId)207     private String refreshCacheForUser(int userId) {
208         String currentDefaultDialer =
209                 mDefaultDialerManagerAdapter.getDefaultDialerApplication(mContext, userId);
210         synchronized (mLock) {
211             mCurrentDefaultDialerPerUser.put(userId, currentDefaultDialer);
212         }
213         return currentDefaultDialer;
214     }
215 
216     /**
217      * Refreshes the cache for users that currently have packageName as their cached default dialer.
218      * If packageName is null, refresh all caches.
219      * @param packageName Name of the affected package.
220      */
refreshCachesForUsersWithPackage(String packageName)221     private void refreshCachesForUsersWithPackage(String packageName) {
222         for (int i = 0; i < mCurrentDefaultDialerPerUser.size(); i++) {
223             int userId = mCurrentDefaultDialerPerUser.keyAt(i);
224             if (packageName == null ||
225                     Objects.equals(packageName, mCurrentDefaultDialerPerUser.get(userId))) {
226                 String newDefaultDialer = refreshCacheForUser(userId);
227                 Log.i(LOG_TAG, "Refreshing default dialer for user %d: now %s",
228                         userId, newDefaultDialer);
229             }
230         }
231     }
232 
dumpCache(IndentingPrintWriter pw)233     public void dumpCache(IndentingPrintWriter pw) {
234         synchronized (mLock) {
235             for (int i = 0; i < mCurrentDefaultDialerPerUser.size(); i++) {
236                 pw.printf("User %d: %s\n", mCurrentDefaultDialerPerUser.keyAt(i),
237                         mCurrentDefaultDialerPerUser.valueAt(i));
238             }
239         }
240     }
241 
removeUserFromCache(int userId)242     private void removeUserFromCache(int userId) {
243         synchronized (mLock) {
244             mCurrentDefaultDialerPerUser.remove(userId);
245         }
246     }
247 
248     /**
249      * registerContentObserver is really hard to mock out, so here is a getter method for the
250      * content observer for testing instead.
251      * @return The content observer
252      */
253     @VisibleForTesting
getContentObserver()254     public ContentObserver getContentObserver() {
255         return mDefaultDialerObserver;
256     }
257 }