• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.ims.rcs.uce.eab;
18 
19 import static android.telephony.ims.RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS;
20 import static android.telephony.ims.RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE;
21 
22 import android.app.AlarmManager;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.database.ContentObserver;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.PersistableBundle;
33 import android.os.RemoteException;
34 import android.provider.ContactsContract;
35 import android.provider.Telephony;
36 import android.telephony.CarrierConfigManager;
37 import android.telephony.ims.ImsManager;
38 import android.telephony.ims.ImsRcsManager;
39 import android.telephony.ims.RcsContactUceCapability;
40 import android.telephony.ims.SipDetails;
41 import android.telephony.ims.aidl.IRcsUceControllerCallback;
42 import android.util.Log;
43 
44 import com.android.ims.rcs.uce.UceController;
45 
46 import java.util.ArrayList;
47 import java.util.List;
48 
49 public final class EabBulkCapabilityUpdater {
50     private final String TAG = this.getClass().getSimpleName();
51 
52     private static final Uri USER_EAB_SETTING = Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
53             Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED);
54     private static final int NUM_SECS_IN_DAY = 86400;
55 
56     private final int mSubId;
57     private final Context mContext;
58     private final Handler mHandler;
59 
60     private final AlarmManager.OnAlarmListener mCapabilityExpiredListener;
61     private final ContactChangedListener mContactProviderListener;
62     private final EabSettingsListener mEabSettingListener;
63     private final EabControllerImpl mEabControllerImpl;
64     private final EabContactSyncController mEabContactSyncController;
65 
66     private UceController.UceControllerCallback mUceControllerCallback;
67     private List<Uri> mRefreshContactList;
68 
69     private boolean mIsContactProviderListenerRegistered = false;
70     private boolean mIsEabSettingListenerRegistered = false;
71     private boolean mIsCarrierConfigListenerRegistered = false;
72     private boolean mIsCarrierConfigEnabled = false;
73 
74     /**
75      * Listen capability expired intent. Only registered when
76      * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} has enabled bulk
77      * capability exchange.
78      */
79     private class CapabilityExpiredListener implements AlarmManager.OnAlarmListener {
80         @Override
onAlarm()81         public void onAlarm() {
82             Log.d(TAG, "Capability expired.");
83             try {
84                 List<Uri> expiredContactList = getExpiredContactList();
85                 if (expiredContactList.size() > 0) {
86                     mUceControllerCallback.refreshCapabilities(
87                             getExpiredContactList(),
88                             mRcsUceControllerCallback);
89                 } else {
90                     Log.d(TAG, "expiredContactList is empty.");
91                 }
92             } catch (RemoteException e) {
93                 Log.e(TAG, "CapabilityExpiredListener RemoteException", e);
94             }
95         }
96     }
97 
98     /**
99      * Listen contact provider change. Only registered when
100      * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} has enabled bulk
101      * capability exchange.
102      */
103     private class ContactChangedListener extends ContentObserver {
ContactChangedListener(Handler handler)104         public ContactChangedListener(Handler handler) {
105             super(handler);
106         }
107 
108         @Override
onChange(boolean selfChange)109         public void onChange(boolean selfChange) {
110             Log.d(TAG, "Contact changed");
111             syncContactAndRefreshCapabilities();
112         }
113     }
114 
115     /**
116      * Listen EAB settings change. Only registered when
117      * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} has enabled bulk
118      * capability exchange.
119      */
120     private class EabSettingsListener extends ContentObserver {
EabSettingsListener(Handler handler)121         public EabSettingsListener(Handler handler) {
122             super(handler);
123         }
124 
125         @Override
onChange(boolean selfChange)126         public void onChange(boolean selfChange) {
127             boolean isUserEnableUce = isUserEnableUce();
128             Log.d(TAG, "EAB user setting changed: " + isUserEnableUce);
129             if (isUserEnableUce) {
130                 mHandler.post(new SyncContactRunnable());
131             } else {
132                 unRegisterContactProviderListener();
133                 cancelTimeAlert(mContext);
134             }
135         }
136     }
137 
138     private IRcsUceControllerCallback mRcsUceControllerCallback = new IRcsUceControllerCallback() {
139         @Override
140         public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
141             Log.d(TAG, "onCapabilitiesReceived");
142             mEabControllerImpl.saveCapabilities(contactCapabilities);
143         }
144 
145         @Override
146         public void onComplete(SipDetails details) {
147             Log.d(TAG, "onComplete");
148         }
149 
150         @Override
151         public void onError(int errorCode, long retryAfterMilliseconds, SipDetails details) {
152             Log.d(TAG, "Refresh capabilities failed. Error code: " + errorCode
153                     + ", retryAfterMilliseconds: " + retryAfterMilliseconds);
154             if (retryAfterMilliseconds != 0) {
155                 mHandler.postDelayed(new retryRunnable(), retryAfterMilliseconds);
156             }
157         }
158 
159         @Override
160         public IBinder asBinder() {
161             return null;
162         }
163     };
164 
165     private class SyncContactRunnable implements Runnable {
166         @Override
run()167         public void run() {
168             Log.d(TAG, "Sync contact from contact provider");
169             syncContactAndRefreshCapabilities();
170             registerContactProviderListener();
171             registerEabUserSettingsListener();
172         }
173     }
174 
175     /**
176      * Re-refresh capability if error happened.
177      */
178     private class retryRunnable implements Runnable {
179         @Override
run()180         public void run() {
181             Log.d(TAG, "Retry refreshCapabilities()");
182 
183             try {
184                 mUceControllerCallback.refreshCapabilities(
185                         mRefreshContactList, mRcsUceControllerCallback);
186             } catch (RemoteException e) {
187                 Log.e(TAG, "refreshCapabilities RemoteException" , e);
188             }
189         }
190     }
191 
EabBulkCapabilityUpdater(Context context, int subId, EabControllerImpl eabControllerImpl, EabContactSyncController eabContactSyncController, UceController.UceControllerCallback uceControllerCallback, Handler handler)192     public EabBulkCapabilityUpdater(Context context,
193             int subId,
194             EabControllerImpl eabControllerImpl,
195             EabContactSyncController eabContactSyncController,
196             UceController.UceControllerCallback uceControllerCallback,
197             Handler handler) {
198         mContext = context;
199         mSubId = subId;
200         mEabControllerImpl = eabControllerImpl;
201         mEabContactSyncController = eabContactSyncController;
202         mUceControllerCallback = uceControllerCallback;
203 
204         mHandler = handler;
205         mContactProviderListener = new ContactChangedListener(mHandler);
206         mEabSettingListener = new EabSettingsListener(mHandler);
207         mCapabilityExpiredListener = new CapabilityExpiredListener();
208 
209         Log.d(TAG, "create EabBulkCapabilityUpdater() subId: " + mSubId);
210 
211         enableBulkCapability();
212         updateExpiredTimeAlert();
213     }
214 
enableBulkCapability()215     private void enableBulkCapability() {
216         boolean isUserEnableUce = isUserEnableUce();
217         boolean isSupportBulkCapabilityExchange = getBooleanCarrierConfig(
218                 CarrierConfigManager.Ims.KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, mSubId);
219 
220         Log.d(TAG, "isUserEnableUce: " + isUserEnableUce
221                 + ", isSupportBulkCapabilityExchange: " + isSupportBulkCapabilityExchange);
222 
223         if (isUserEnableUce && isSupportBulkCapabilityExchange) {
224             mHandler.post(new SyncContactRunnable());
225             mIsCarrierConfigEnabled = true;
226         } else if (!isUserEnableUce && isSupportBulkCapabilityExchange) {
227             registerEabUserSettingsListener();
228             mIsCarrierConfigEnabled = false;
229         } else {
230             Log.d(TAG, "Not support bulk capability exchange.");
231         }
232     }
233 
syncContactAndRefreshCapabilities()234     private void syncContactAndRefreshCapabilities() {
235         mRefreshContactList = mEabContactSyncController.syncContactToEabProvider(mContext);
236         Log.d(TAG, "refresh contacts number: " + mRefreshContactList.size());
237 
238         if (mUceControllerCallback == null) {
239             Log.d(TAG, "mUceControllerCallback is null.");
240             return;
241         }
242 
243         try {
244             if (mRefreshContactList.size() > 0) {
245                 mUceControllerCallback.refreshCapabilities(
246                         mRefreshContactList, mRcsUceControllerCallback);
247             }
248         } catch (RemoteException e) {
249             Log.e(TAG, "mUceControllerCallback RemoteException.", e);
250         }
251     }
252 
updateExpiredTimeAlert()253     protected void updateExpiredTimeAlert() {
254         boolean isUserEnableUce = isUserEnableUce();
255         boolean isSupportBulkCapabilityExchange = getBooleanCarrierConfig(
256                 CarrierConfigManager.Ims.KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, mSubId);
257 
258         Log.d(TAG, " updateExpiredTimeAlert(), isUserEnableUce: " + isUserEnableUce
259                 + ", isSupportBulkCapabilityExchange: " + isSupportBulkCapabilityExchange);
260 
261         if (isUserEnableUce && isSupportBulkCapabilityExchange) {
262             long expiredTimestamp = getLeastExpiredTimestamp();
263             if (expiredTimestamp == Long.MAX_VALUE) {
264                 Log.d(TAG, "Can't find min timestamp in eab provider");
265                 return;
266             }
267             expiredTimestamp += mEabControllerImpl.getCapabilityCacheExpiration(mSubId);
268             Log.d(TAG, "set time alert at " + expiredTimestamp);
269             cancelTimeAlert(mContext);
270             setTimeAlert(mContext, expiredTimestamp);
271         }
272     }
273 
getLeastExpiredTimestamp()274     private long getLeastExpiredTimestamp() {
275         String selection = "("
276                 // Query presence timestamp
277                 + EabProvider.EabCommonColumns.MECHANISM + "=" + CAPABILITY_MECHANISM_PRESENCE
278                 + " AND "
279                 + EabProvider.PresenceTupleColumns.REQUEST_TIMESTAMP + " IS NOT NULL) "
280 
281                 // Query options timestamp
282                 + " OR " + "(" + EabProvider.EabCommonColumns.MECHANISM + "="
283                 + CAPABILITY_MECHANISM_OPTIONS + " AND "
284                 + EabProvider.OptionsColumns.REQUEST_TIMESTAMP + " IS NOT NULL) "
285 
286                 // filter by sub id
287                 + " AND " + EabProvider.EabCommonColumns.SUBSCRIPTION_ID + "=" + mSubId
288 
289                 // filter the contact that not come from contact provider
290                 + " AND " + EabProvider.ContactColumns.RAW_CONTACT_ID + " IS NOT NULL "
291                 + " AND " + EabProvider.ContactColumns.DATA_ID + " IS NOT NULL ";
292 
293         long minTimestamp = Long.MAX_VALUE;
294         Cursor result = mContext.getContentResolver().query(EabProvider.ALL_DATA_URI, null,
295                 selection,
296                 null, null);
297 
298         if (result != null) {
299             while (result.moveToNext()) {
300                 int mechanism = result.getInt(
301                         result.getColumnIndex(EabProvider.EabCommonColumns.MECHANISM));
302                 long timestamp;
303                 if (mechanism == CAPABILITY_MECHANISM_PRESENCE) {
304                     timestamp = result.getLong(result.getColumnIndex(
305                             EabProvider.PresenceTupleColumns.REQUEST_TIMESTAMP));
306                 } else {
307                     timestamp = result.getLong(result.getColumnIndex(
308                             EabProvider.OptionsColumns.REQUEST_TIMESTAMP));
309                 }
310 
311                 if (timestamp < minTimestamp) {
312                     minTimestamp = timestamp;
313                 }
314             }
315             result.close();
316         } else {
317             Log.d(TAG, "getLeastExpiredTimestamp() cursor is null");
318         }
319         return minTimestamp;
320     }
321 
setTimeAlert(Context context, long wakeupTimeMs)322     private void setTimeAlert(Context context, long wakeupTimeMs) {
323         AlarmManager am = context.getSystemService(AlarmManager.class);
324 
325         // To prevent all devices from sending requests to the server at the same time, add a jitter
326         // time (0 sec ~ 2 days) randomly.
327         int jitterTimeSec = (int) (Math.random() * (NUM_SECS_IN_DAY * 2));
328         Log.d(TAG, " setTimeAlert: " + wakeupTimeMs + ", jitterTimeSec: " + jitterTimeSec);
329         am.set(AlarmManager.RTC_WAKEUP,
330                 (wakeupTimeMs * 1000) + jitterTimeSec,
331                 TAG,
332                 mCapabilityExpiredListener,
333                 mHandler);
334     }
335 
cancelTimeAlert(Context context)336     private void cancelTimeAlert(Context context) {
337         Log.d(TAG, "cancelTimeAlert.");
338         AlarmManager am = context.getSystemService(AlarmManager.class);
339         am.cancel(mCapabilityExpiredListener);
340     }
341 
getBooleanCarrierConfig(String key, int subId)342     private boolean getBooleanCarrierConfig(String key, int subId) {
343         CarrierConfigManager mConfigManager = mContext.getSystemService(CarrierConfigManager.class);
344         PersistableBundle b = null;
345         if (mConfigManager != null) {
346             b = mConfigManager.getConfigForSubId(subId);
347         }
348         if (b != null) {
349             return b.getBoolean(key);
350         } else {
351             Log.w(TAG, "getConfigForSubId(subId) is null. Return the default value of " + key);
352             return CarrierConfigManager.getDefaultConfig().getBoolean(key);
353         }
354     }
355 
isUserEnableUce()356     private boolean isUserEnableUce() {
357         ImsManager manager = mContext.getSystemService(ImsManager.class);
358         if (manager == null) {
359             Log.e(TAG, "ImsManager is null");
360             return false;
361         }
362         try {
363             ImsRcsManager rcsManager = manager.getImsRcsManager(mSubId);
364             return (rcsManager != null) && rcsManager.getUceAdapter().isUceSettingEnabled();
365         } catch (Exception e) {
366             Log.e(TAG, "hasUserEnabledUce: exception = " + e.getMessage());
367         }
368         return false;
369     }
370 
getExpiredContactList()371     private List<Uri> getExpiredContactList() {
372         List<Uri> refreshList = new ArrayList<>();
373         long expiredTime = (System.currentTimeMillis() / 1000)
374                 + mEabControllerImpl.getCapabilityCacheExpiration(mSubId);
375         String selection = "("
376                 + EabProvider.EabCommonColumns.MECHANISM + "=" + CAPABILITY_MECHANISM_PRESENCE
377                 + " AND " + EabProvider.PresenceTupleColumns.REQUEST_TIMESTAMP + "<"
378                 + expiredTime + ")";
379         selection += " OR " + "(" + EabProvider.EabCommonColumns.MECHANISM + "="
380                 + CAPABILITY_MECHANISM_OPTIONS + " AND "
381                 + EabProvider.OptionsColumns.REQUEST_TIMESTAMP + "<" + expiredTime + ")";
382 
383         Cursor result = mContext.getContentResolver().query(EabProvider.ALL_DATA_URI, null,
384                 selection,
385                 null, null);
386         while (result.moveToNext()) {
387             String phoneNumber = result.getString(
388                     result.getColumnIndex(EabProvider.ContactColumns.PHONE_NUMBER));
389             refreshList.add(Uri.parse(phoneNumber));
390         }
391         result.close();
392         return refreshList;
393     }
394 
onDestroy()395     protected void onDestroy() {
396         Log.d(TAG, "onDestroy");
397         cancelTimeAlert(mContext);
398         unRegisterContactProviderListener();
399         unRegisterEabUserSettings();
400         mIsCarrierConfigEnabled = false;
401     }
402 
registerContactProviderListener()403     private void registerContactProviderListener() {
404         Log.d(TAG, "registerContactProviderListener");
405         mIsContactProviderListenerRegistered = true;
406         mContext.getContentResolver().registerContentObserver(
407                 ContactsContract.Contacts.CONTENT_URI,
408                 true,
409                 mContactProviderListener);
410     }
411 
registerEabUserSettingsListener()412     private void registerEabUserSettingsListener() {
413         Log.d(TAG, "registerEabUserSettingsListener");
414         mIsEabSettingListenerRegistered = true;
415         mContext.getContentResolver().registerContentObserver(
416                 USER_EAB_SETTING,
417                 true,
418                 mEabSettingListener);
419     }
420 
unRegisterContactProviderListener()421     private void unRegisterContactProviderListener() {
422         Log.d(TAG, "unRegisterContactProviderListener");
423         if (mIsContactProviderListenerRegistered) {
424             mIsContactProviderListenerRegistered = false;
425             mContext.getContentResolver().unregisterContentObserver(mContactProviderListener);
426         }
427     }
428 
unRegisterEabUserSettings()429     private void unRegisterEabUserSettings() {
430         Log.d(TAG, "unRegisterEabUserSettings");
431         if (mIsEabSettingListenerRegistered) {
432             mIsEabSettingListenerRegistered = false;
433             mContext.getContentResolver().unregisterContentObserver(mEabSettingListener);
434         }
435     }
436 
setUceRequestCallback(UceController.UceControllerCallback uceControllerCallback)437     public void setUceRequestCallback(UceController.UceControllerCallback uceControllerCallback) {
438         mUceControllerCallback = uceControllerCallback;
439     }
440 
onCarrierConfigChanged()441     public void onCarrierConfigChanged() {
442         boolean isSupportBulkCapabilityExchange = getBooleanCarrierConfig(
443                 CarrierConfigManager.Ims.KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, mSubId);
444 
445         Log.d(TAG, "Carrier config changed. "
446                 + "isCarrierConfigEnabled: " + mIsCarrierConfigEnabled
447                 + ", isSupportBulkCapabilityExchange: " + isSupportBulkCapabilityExchange);
448         if (!mIsCarrierConfigEnabled && isSupportBulkCapabilityExchange) {
449             enableBulkCapability();
450             updateExpiredTimeAlert();
451             mIsCarrierConfigEnabled = true;
452         } else if (mIsCarrierConfigEnabled && !isSupportBulkCapabilityExchange) {
453             onDestroy();
454         }
455     }
456 }
457