• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (C) 2014 Samsung System LSI
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package com.android.bluetooth.map;
16 
17 import java.util.ArrayList;
18 import java.util.LinkedHashMap;
19 import java.util.List;
20 
21 import android.content.BroadcastReceiver;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.PackageInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.PackageManager.NameNotFoundException;
29 import android.content.pm.ResolveInfo;
30 import android.database.ContentObserver;
31 import android.net.Uri;
32 import android.os.Handler;
33 import com.android.bluetooth.mapapi.BluetoothMapContract;
34 import android.util.Log;
35 
36 /**
37  * Class to construct content observers for for email applications on the system.
38  *
39  *
40  */
41 
42 public class BluetoothMapAppObserver{
43 
44     private static final String TAG = "BluetoothMapAppObserver";
45 
46     private static final boolean D = BluetoothMapService.DEBUG;
47     private static final boolean V = BluetoothMapService.VERBOSE;
48     /*  */
49     private LinkedHashMap<BluetoothMapAccountItem, ArrayList<BluetoothMapAccountItem>> mFullList;
50     private LinkedHashMap<String,ContentObserver> mObserverMap =
51             new LinkedHashMap<String,ContentObserver>();
52     private ContentResolver mResolver;
53     private Context mContext;
54     private BroadcastReceiver mReceiver;
55     private PackageManager mPackageManager = null;
56     BluetoothMapAccountLoader mLoader;
57     BluetoothMapService mMapService = null;
58 
BluetoothMapAppObserver(final Context context, BluetoothMapService mapService)59     public BluetoothMapAppObserver(final Context context, BluetoothMapService mapService) {
60         mContext    = context;
61         mMapService = mapService;
62         mResolver   = context.getContentResolver();
63         mLoader     = new BluetoothMapAccountLoader(mContext);
64         mFullList   = mLoader.parsePackages(false); /* Get the current list of apps */
65         createReceiver();
66         initObservers();
67     }
68 
69 
getApp(String authoritiesName)70     private BluetoothMapAccountItem getApp(String authoritiesName) {
71         if(V) Log.d(TAG, "getApp(): Looking for " + authoritiesName);
72         for(BluetoothMapAccountItem app:mFullList.keySet()){
73             if(V) Log.d(TAG, "  Comparing: " + app.getProviderAuthority());
74             if(app.getProviderAuthority().equals(authoritiesName)) {
75                 if(V) Log.d(TAG, "  found " + app.mBase_uri_no_account);
76                 return app;
77             }
78         }
79         if(V) Log.d(TAG, "  NOT FOUND!");
80         return null;
81     }
82 
handleAccountChanges(String packageNameWithProvider)83     private void handleAccountChanges(String packageNameWithProvider) {
84 
85         if(D)Log.d(TAG,"handleAccountChanges (packageNameWithProvider: "
86                         +packageNameWithProvider+"\n");
87         //String packageName = packageNameWithProvider.replaceFirst("\\.[^\\.]+$", "");
88         BluetoothMapAccountItem app = getApp(packageNameWithProvider);
89         if(app != null) {
90             ArrayList<BluetoothMapAccountItem> newAccountList = mLoader.parseAccounts(app);
91             ArrayList<BluetoothMapAccountItem> oldAccountList = mFullList.get(app);
92             ArrayList<BluetoothMapAccountItem> addedAccountList =
93                     (ArrayList<BluetoothMapAccountItem>)newAccountList.clone();
94             ArrayList<BluetoothMapAccountItem> removedAccountList = mFullList.get(app);
95             // Same as oldAccountList.clone
96 
97             mFullList.put(app, newAccountList);
98             for(BluetoothMapAccountItem newAcc: newAccountList){
99                 for(BluetoothMapAccountItem oldAcc: oldAccountList){
100                     if(newAcc.getId() == oldAcc.getId()){
101                         // For each match remove from both removed and added lists
102                         removedAccountList.remove(oldAcc);
103                         addedAccountList.remove(newAcc);
104                         if(!newAcc.getName().equals(oldAcc.getName()) && newAcc.mIsChecked){
105                             // Name Changed and the acc is visible - Change Name in SDP record
106                             mMapService.updateMasInstances(
107                                     BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_RENAMED);
108                             if(V)Log.d(TAG, "    UPDATE_MAS_INSTANCES_ACCOUNT_RENAMED");
109                         }
110                         if(newAcc.mIsChecked != oldAcc.mIsChecked) {
111                             // Visibility changed
112                             if(newAcc.mIsChecked){
113                                 // account added - create SDP record
114                                 mMapService.updateMasInstances(
115                                         BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_ADDED);
116                                 if(V)Log.d(TAG, "UPDATE_MAS_INSTANCES_ACCOUNT_ADDED " +
117                                         "isChecked changed");
118                             } else {
119                                 // account removed - remove SDP record
120                                 mMapService.updateMasInstances(
121                                         BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_REMOVED);
122                                 if(V)Log.d(TAG, "    UPDATE_MAS_INSTANCES_ACCOUNT_REMOVED " +
123                                         "isChecked changed");
124                             }
125                         }
126                         break;
127                     }
128                 }
129             }
130             // Notify on any removed accounts
131             for(BluetoothMapAccountItem removedAcc: removedAccountList){
132                 mMapService.updateMasInstances(
133                         BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_REMOVED);
134                 if(V)Log.d(TAG, "    UPDATE_MAS_INSTANCES_ACCOUNT_REMOVED " + removedAcc);
135             }
136             // Notify on any new accounts
137             for(BluetoothMapAccountItem addedAcc: addedAccountList){
138                 mMapService.updateMasInstances(
139                         BluetoothMapService.UPDATE_MAS_INSTANCES_ACCOUNT_ADDED);
140                 if(V)Log.d(TAG, "    UPDATE_MAS_INSTANCES_ACCOUNT_ADDED " + addedAcc);
141             }
142 
143         } else {
144             Log.e(TAG, "Received change notification on package not registered for notifications!");
145 
146         }
147     }
148 
149     /**
150      * Adds a new content observer to the list of content observers.
151      * The key for the observer is the uri as string
152      * @param uri uri for the package that supports MAP email
153      */
154 
registerObserver(BluetoothMapAccountItem app)155     public void registerObserver(BluetoothMapAccountItem app) {
156         Uri uri = BluetoothMapContract.buildAccountUri(app.getProviderAuthority());
157         if (V) Log.d(TAG, "registerObserver for URI "+uri.toString()+"\n");
158         ContentObserver observer = new ContentObserver(new Handler()) {
159             @Override
160             public void onChange(boolean selfChange) {
161                 onChange(selfChange, null);
162             }
163 
164             @Override
165             public void onChange(boolean selfChange, Uri uri) {
166                 if (V) Log.d(TAG, "onChange on thread: " + Thread.currentThread().getId()
167                         + " Uri: " + uri + " selfchange: " + selfChange);
168                 if(uri != null) {
169                     handleAccountChanges(uri.getHost());
170                 } else {
171                     Log.e(TAG, "Unable to handle change as the URI is NULL!");
172                 }
173 
174             }
175         };
176         mObserverMap.put(uri.toString(), observer);
177         mResolver.registerContentObserver(uri, true, observer);
178     }
179 
unregisterObserver(BluetoothMapAccountItem app)180     public void unregisterObserver(BluetoothMapAccountItem app) {
181         Uri uri = BluetoothMapContract.buildAccountUri(app.getProviderAuthority());
182         if (V) Log.d(TAG, "unregisterObserver("+uri.toString()+")\n");
183         mResolver.unregisterContentObserver(mObserverMap.get(uri.toString()));
184         mObserverMap.remove(uri.toString());
185     }
186 
initObservers()187     private void initObservers(){
188         if(D)Log.d(TAG,"initObservers()");
189         for(BluetoothMapAccountItem app: mFullList.keySet()){
190             registerObserver(app);
191         }
192     }
193 
deinitObservers()194     private void deinitObservers(){
195         if(D)Log.d(TAG,"deinitObservers()");
196         for(BluetoothMapAccountItem app: mFullList.keySet()){
197             unregisterObserver(app);
198         }
199     }
200 
createReceiver()201     private void createReceiver(){
202         if(D)Log.d(TAG,"createReceiver()\n");
203         IntentFilter intentFilter = new IntentFilter();
204         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
205         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
206         intentFilter.addDataScheme("package");
207         mReceiver = new BroadcastReceiver() {
208             @Override
209             public void onReceive(Context context, Intent intent) {
210                 if(D)Log.d(TAG,"onReceive\n");
211                 String action = intent.getAction();
212 
213                 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
214                     Uri data = intent.getData();
215                     String packageName = data.getEncodedSchemeSpecificPart();
216                     if(D)Log.d(TAG,"The installed package is: "+ packageName);
217 
218                     BluetoothMapUtils.TYPE msgType = BluetoothMapUtils.TYPE.NONE;
219                     ResolveInfo resolveInfo = null;
220                     Intent[] searchIntents = new Intent[2];
221                     //Array <Intent> searchIntents = new Array <Intent>();
222                     searchIntents[0] = new Intent(BluetoothMapContract.PROVIDER_INTERFACE_EMAIL);
223                     searchIntents[1] = new Intent(BluetoothMapContract.PROVIDER_INTERFACE_IM);
224                     // Find all installed packages and filter out those that support Bluetooth Map.
225 
226                     mPackageManager = mContext.getPackageManager();
227 
228                     for (Intent searchIntent : searchIntents) {
229                         List<ResolveInfo> resInfos =
230                                 mPackageManager.queryIntentContentProviders(searchIntent, 0);
231                         if (resInfos != null ) {
232                             if(D) Log.d(TAG,"Found " + resInfos.size()
233                                     + " application(s) with intent "
234                                     + searchIntent.getAction().toString());
235                             for (ResolveInfo rInfo : resInfos) {
236                                 if(rInfo != null) {
237                                     // Find out if package contain Bluetooth MAP support
238                                     if (packageName.equals(rInfo.providerInfo.packageName)) {
239                                         resolveInfo = rInfo;
240                                         if(searchIntent.getAction() ==
241                                                 BluetoothMapContract.PROVIDER_INTERFACE_EMAIL){
242                                             msgType = BluetoothMapUtils.TYPE.EMAIL;
243                                         } else if (searchIntent.getAction() ==
244                                                 BluetoothMapContract.PROVIDER_INTERFACE_IM){
245                                             msgType = BluetoothMapUtils.TYPE.IM;
246                                         }
247                                         break;
248                                     }
249                                 }
250                             }
251                         }
252                     }
253                     // if application found with Bluetooth MAP support add to list
254                     if(resolveInfo != null) {
255                         if(D) Log.d(TAG,"Found " + resolveInfo.providerInfo.packageName
256                                 + " application of type " + msgType);
257                         BluetoothMapAccountItem app = mLoader.createAppItem(resolveInfo,
258                                 false, msgType);
259                         if(app != null) {
260                             registerObserver(app);
261                             // Add all accounts to mFullList
262                             ArrayList<BluetoothMapAccountItem> newAccountList =
263                                     mLoader.parseAccounts(app);
264                             mFullList.put(app, newAccountList);
265                         }
266                     }
267 
268                 }
269                 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
270                     Uri data = intent.getData();
271                     String packageName = data.getEncodedSchemeSpecificPart();
272                     if(D)Log.d(TAG,"The removed package is: "+ packageName);
273                     BluetoothMapAccountItem app = getApp(packageName);
274                     /* Find the object and remove from fullList */
275                     if(app != null) {
276                         unregisterObserver(app);
277                         mFullList.remove(app);
278                     }
279                 }
280             }
281         };
282         mContext.registerReceiver(mReceiver,intentFilter);
283     }
284 
removeReceiver()285     private void removeReceiver(){
286         if(D)Log.d(TAG,"removeReceiver()\n");
287         mContext.unregisterReceiver(mReceiver);
288     }
289 
290     /**
291      * Method to get a list of the accounts (across all apps) that are set to be shared
292      * through MAP.
293      * @return Arraylist<BluetoothMapAccountItem> containing all enabled accounts
294      */
getEnabledAccountItems()295     public ArrayList<BluetoothMapAccountItem> getEnabledAccountItems(){
296         if(D)Log.d(TAG,"getEnabledAccountItems()\n");
297         ArrayList<BluetoothMapAccountItem> list = new ArrayList<BluetoothMapAccountItem>();
298         for(BluetoothMapAccountItem app:mFullList.keySet()){
299             ArrayList<BluetoothMapAccountItem> accountList = mFullList.get(app);
300             for(BluetoothMapAccountItem acc: accountList){
301                 if(acc.mIsChecked) {
302                     list.add(acc);
303                 }
304             }
305         }
306         return list;
307     }
308 
309     /**
310      * Method to get a list of the accounts (across all apps).
311      * @return Arraylist<BluetoothMapAccountItem> containing all accounts
312      */
getAllAccountItems()313     public ArrayList<BluetoothMapAccountItem> getAllAccountItems(){
314         if(D)Log.d(TAG,"getAllAccountItems()\n");
315         ArrayList<BluetoothMapAccountItem> list = new ArrayList<BluetoothMapAccountItem>();
316         for(BluetoothMapAccountItem app:mFullList.keySet()){
317             ArrayList<BluetoothMapAccountItem> accountList = mFullList.get(app);
318             list.addAll(accountList);
319         }
320         return list;
321     }
322 
323 
324     /**
325      * Cleanup all resources - must be called to avoid leaks.
326      */
shutdown()327     public void shutdown() {
328         deinitObservers();
329         removeReceiver();
330     }
331 }
332