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.messaging.datamodel;
18 
19 import android.content.Context;
20 import android.database.sqlite.SQLiteDatabase;
21 import android.net.Uri;
22 import android.telephony.SubscriptionManager;
23 
24 import com.android.messaging.datamodel.action.ActionService;
25 import com.android.messaging.datamodel.action.BackgroundWorker;
26 import com.android.messaging.datamodel.action.FixupMessageStatusOnStartupAction;
27 import com.android.messaging.datamodel.action.ProcessPendingMessagesAction;
28 import com.android.messaging.datamodel.data.BlockedParticipantsData;
29 import com.android.messaging.datamodel.data.BlockedParticipantsData.BlockedParticipantsDataListener;
30 import com.android.messaging.datamodel.data.ContactListItemData;
31 import com.android.messaging.datamodel.data.ContactPickerData;
32 import com.android.messaging.datamodel.data.ContactPickerData.ContactPickerDataListener;
33 import com.android.messaging.datamodel.data.ConversationData;
34 import com.android.messaging.datamodel.data.ConversationData.ConversationDataListener;
35 import com.android.messaging.datamodel.data.ConversationListData;
36 import com.android.messaging.datamodel.data.ConversationListData.ConversationListDataListener;
37 import com.android.messaging.datamodel.data.DraftMessageData;
38 import com.android.messaging.datamodel.data.GalleryGridItemData;
39 import com.android.messaging.datamodel.data.LaunchConversationData;
40 import com.android.messaging.datamodel.data.LaunchConversationData.LaunchConversationDataListener;
41 import com.android.messaging.datamodel.data.MediaPickerData;
42 import com.android.messaging.datamodel.data.MessagePartData;
43 import com.android.messaging.datamodel.data.ParticipantData;
44 import com.android.messaging.datamodel.data.ParticipantListItemData;
45 import com.android.messaging.datamodel.data.PeopleAndOptionsData;
46 import com.android.messaging.datamodel.data.PeopleAndOptionsData.PeopleAndOptionsDataListener;
47 import com.android.messaging.datamodel.data.PeopleOptionsItemData;
48 import com.android.messaging.datamodel.data.SettingsData;
49 import com.android.messaging.datamodel.data.SettingsData.SettingsDataListener;
50 import com.android.messaging.datamodel.data.SubscriptionListData;
51 import com.android.messaging.datamodel.data.VCardContactItemData;
52 import com.android.messaging.sms.MmsConfig;
53 import com.android.messaging.util.Assert;
54 import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
55 import com.android.messaging.util.ConnectivityUtil;
56 import com.android.messaging.util.LogUtil;
57 import com.android.messaging.util.OsUtil;
58 import com.android.messaging.util.PhoneUtils;
59 
60 import java.util.concurrent.ConcurrentHashMap;
61 
62 public class DataModelImpl extends DataModel {
63     private final Context mContext;
64     private final ActionService mActionService;
65     private final BackgroundWorker mDataModelWorker;
66     private final DatabaseHelper mDatabaseHelper;
67     private final SyncManager mSyncManager;
68 
69     // Cached ConnectivityUtil instance for Pre-N.
70     private static ConnectivityUtil sConnectivityUtilInstanceCachePreN = null;
71     // Cached ConnectivityUtil subId->instance for N and beyond
72     private static final ConcurrentHashMap<Integer, ConnectivityUtil>
73             sConnectivityUtilInstanceCacheN = new ConcurrentHashMap<>();
74 
DataModelImpl(final Context context)75     public DataModelImpl(final Context context) {
76         super();
77         mContext = context;
78         mActionService = new ActionService();
79         mDataModelWorker = new BackgroundWorker();
80         mDatabaseHelper = DatabaseHelper.getInstance(context);
81         mSyncManager = new SyncManager();
82     }
83 
84     @Override
createConversationListData(final Context context, final ConversationListDataListener listener, final boolean archivedMode)85     public ConversationListData createConversationListData(final Context context,
86             final ConversationListDataListener listener, final boolean archivedMode) {
87         return new ConversationListData(context, listener, archivedMode);
88     }
89 
90     @Override
createConversationData(final Context context, final ConversationDataListener listener, final String conversationId)91     public ConversationData createConversationData(final Context context,
92             final ConversationDataListener listener, final String conversationId) {
93         return new ConversationData(context, listener, conversationId);
94     }
95 
96     @Override
createContactListItemData()97     public ContactListItemData createContactListItemData() {
98         return new ContactListItemData();
99     }
100 
101     @Override
createContactPickerData(final Context context, final ContactPickerDataListener listener)102     public ContactPickerData createContactPickerData(final Context context,
103             final ContactPickerDataListener listener) {
104         return new ContactPickerData(context, listener);
105     }
106 
107     @Override
createBlockedParticipantsData( final Context context, final BlockedParticipantsDataListener listener)108     public BlockedParticipantsData createBlockedParticipantsData(
109             final Context context, final BlockedParticipantsDataListener listener) {
110         return new BlockedParticipantsData(context, listener);
111     }
112 
113     @Override
createMediaPickerData(final Context context)114     public MediaPickerData createMediaPickerData(final Context context) {
115         return new MediaPickerData(context);
116     }
117 
118     @Override
createGalleryGridItemData()119     public GalleryGridItemData createGalleryGridItemData() {
120         return new GalleryGridItemData();
121     }
122 
123     @Override
createLaunchConversationData( final LaunchConversationDataListener listener)124     public LaunchConversationData createLaunchConversationData(
125             final LaunchConversationDataListener listener) {
126        return new LaunchConversationData(listener);
127     }
128 
129     @Override
createPeopleOptionsItemData(final Context context)130     public PeopleOptionsItemData createPeopleOptionsItemData(final Context context) {
131         return new PeopleOptionsItemData(context);
132     }
133 
134     @Override
createPeopleAndOptionsData(final String conversationId, final Context context, final PeopleAndOptionsDataListener listener)135     public PeopleAndOptionsData createPeopleAndOptionsData(final String conversationId,
136             final Context context, final PeopleAndOptionsDataListener listener) {
137         return new PeopleAndOptionsData(conversationId, context, listener);
138     }
139 
140     @Override
createVCardContactItemData(final Context context, final MessagePartData data)141     public VCardContactItemData createVCardContactItemData(final Context context,
142             final MessagePartData data) {
143         return new VCardContactItemData(context, data);
144     }
145 
146     @Override
createVCardContactItemData(final Context context, final Uri vCardUri)147     public VCardContactItemData createVCardContactItemData(final Context context,
148             final Uri vCardUri) {
149         return new VCardContactItemData(context, vCardUri);
150     }
151 
152     @Override
createParticipantListItemData( final ParticipantData participant)153     public ParticipantListItemData createParticipantListItemData(
154             final ParticipantData participant) {
155         return new ParticipantListItemData(participant);
156     }
157 
158     @Override
createSubscriptonListData(Context context)159     public SubscriptionListData createSubscriptonListData(Context context) {
160         return new SubscriptionListData(context);
161     }
162 
163     @Override
createSettingsData(Context context, SettingsDataListener listener)164     public SettingsData createSettingsData(Context context, SettingsDataListener listener) {
165         return new SettingsData(context, listener);
166     }
167 
168     @Override
createDraftMessageData(String conversationId)169     public DraftMessageData createDraftMessageData(String conversationId) {
170         return new DraftMessageData(conversationId);
171     }
172 
173     @Override
getActionService()174     public ActionService getActionService() {
175         // We need to allow access to this on the UI thread since it's used to start actions.
176         return mActionService;
177     }
178 
179     @Override
getBackgroundWorkerForActionService()180     public BackgroundWorker getBackgroundWorkerForActionService() {
181         return mDataModelWorker;
182     }
183 
184     @Override
185     @DoesNotRunOnMainThread
getDatabase()186     public DatabaseWrapper getDatabase() {
187         // We prevent the main UI thread from accessing the database since we have to allow
188         // public access to this class to enable sub-packages to access data.
189         Assert.isNotMainThread();
190         return mDatabaseHelper.getDatabase();
191     }
192 
193     @Override
getSyncManager()194     public SyncManager getSyncManager() {
195         return mSyncManager;
196     }
197 
198     @Override
onCreateTables(final SQLiteDatabase db)199     void onCreateTables(final SQLiteDatabase db) {
200         LogUtil.w(LogUtil.BUGLE_TAG, "Rebuilt databases: reseting related state");
201         // Clear other things that implicitly reference the DB
202         SyncManager.resetLastSyncTimestamps();
203     }
204 
205     @Override
onActivityResume()206     public void onActivityResume() {
207         // Perform an incremental sync and register for changes if necessary
208         mSyncManager.updateSyncObserver(mContext);
209 
210         // Trigger a participant refresh if needed, we should only need to refresh if there is
211         // contact change while the activity was paused.
212         ParticipantRefresh.refreshParticipantsIfNeeded();
213     }
214 
215     @Override
onApplicationCreated()216     public void onApplicationCreated() {
217         if (OsUtil.isAtLeastN()) {
218             createConnectivityUtilForEachActiveSubscription();
219         } else {
220             sConnectivityUtilInstanceCachePreN = new ConnectivityUtil(mContext);
221         }
222 
223         FixupMessageStatusOnStartupAction.fixupMessageStatus();
224         ProcessPendingMessagesAction.processFirstPendingMessage();
225         SyncManager.immediateSync();
226 
227         if (OsUtil.isAtLeastL_MR1()) {
228             // Start listening for subscription change events for refreshing any data associated
229             // with subscriptions.
230             PhoneUtils.getDefault().toLMr1().registerOnSubscriptionsChangedListener(
231                     new SubscriptionManager.OnSubscriptionsChangedListener() {
232                         @Override
233                         public void onSubscriptionsChanged() {
234                             // TODO: This dynamically changes the mms config that app is
235                             // currently using. It may cause inconsistency in some cases. We need
236                             // to check the usage of mms config and handle the dynamic change
237                             // gracefully
238                             MmsConfig.loadAsync();
239                             ParticipantRefresh.refreshSelfParticipants();
240                             if (OsUtil.isAtLeastN()) {
241                                 createConnectivityUtilForEachActiveSubscription();
242                             }
243                         }
244                     });
245         }
246     }
247 
createConnectivityUtilForEachActiveSubscription()248     private void createConnectivityUtilForEachActiveSubscription() {
249         PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() {
250             @Override
251             public void runForSubscription(int subId) {
252                 // Create the ConnectivityUtil instance for given subId if absent.
253                 if (subId <= ParticipantData.DEFAULT_SELF_SUB_ID) {
254                     subId = PhoneUtils.getDefault().getDefaultSmsSubscriptionId();
255                 }
256                 if (!sConnectivityUtilInstanceCacheN.containsKey(subId)) {
257                     sConnectivityUtilInstanceCacheN.put(
258                             subId, new ConnectivityUtil(mContext, subId));
259                 }
260             }
261         });
262     }
263 
getConnectivityUtil(final int subId)264     public static ConnectivityUtil getConnectivityUtil(final int subId) {
265         if (OsUtil.isAtLeastN()) {
266             return sConnectivityUtilInstanceCacheN.get(subId);
267         } else {
268             return sConnectivityUtilInstanceCachePreN;
269         }
270     }
271 }
272